This commit is contained in:
启星
2025-08-12 14:27:12 +08:00
parent 9d18b353b1
commit 1bd5e77c45
8785 changed files with 978163 additions and 2 deletions

58
Pods/LLDebugTool/LICENSE generated Normal file
View File

@@ -0,0 +1,58 @@
Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
----------
Anti 996 License Version 1.0 (Draft)
Permission is hereby granted to any individual or legal entity
obtaining a copy of this licensed work (including the source code,
documentation and/or related items, hereinafter collectively referred
to as the "licensed work"), free of charge, to deal with the licensed
work for any purpose, including without limitation, the rights to use,
reproduce, modify, prepare derivative works of, distribute, publish
and sublicense the licensed work, subject to the following conditions:
1. The individual or the legal entity must conspicuously display,
without modification, this License and the notice on each redistributed
or derivative copy of the Licensed Work.
2. The individual or the legal entity must strictly comply with all
applicable laws, regulations, rules and standards of the jurisdiction
relating to labor and employment where the individual is physically
located or where the individual was born or naturalized; or where the
legal entity is registered or is operating (whichever is stricter). In
case that the jurisdiction has no such laws, regulations, rules and
standards or its laws, regulations, rules and standards are
unenforceable, the individual or the legal entity are required to
comply with Core International Labor Standards.
3. The individual or the legal entity shall not induce or force its
employee(s), whether full-time or part-time, or its independent
contractor(s), in any methods, to agree in oral or written form, to
directly or indirectly restrict, weaken or relinquish his or her
rights or remedies under such laws, regulations, rules and standards
relating to labor and employment as mentioned above, no matter whether
such written or oral agreement are enforceable under the laws of the
said jurisdiction, nor shall such individual or the legal entity
limit, in any methods, the rights of its employee(s) or independent
contractor(s) from reporting or complaining to the copyright holder or
relevant authorities monitoring the compliance of the license about
its violation(s) of the said license.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,189 @@
//
// LLAppInfoHelper.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
Notifications will post each second on main thread.
Cpu is between 0.0% to 100.0%.
Memory's unit is byte.
Fps is float between 0.0 to 60.0.
Request data traffic is upload data.
Response data traffic is download data.
Total data traffic is total data.
*/
FOUNDATION_EXPORT NSNotificationName const LLAppInfoHelperDidUpdateAppInfosNotificationName;
FOUNDATION_EXPORT NSString * const LLAppInfoHelperCPUKey;
FOUNDATION_EXPORT NSString * const LLAppInfoHelperMemoryUsedKey;
FOUNDATION_EXPORT NSString * const LLAppInfoHelperMemoryFreeKey;
FOUNDATION_EXPORT NSString * const LLAppInfoHelperMemoryTotalKey;
FOUNDATION_EXPORT NSString * const LLAppInfoHelperFPSKey;
FOUNDATION_EXPORT NSString * const LLAppInfoHelperRequestDataTrafficKey;
FOUNDATION_EXPORT NSString * const LLAppInfoHelperResponseDataTrafficKey;
FOUNDATION_EXPORT NSString * const LLAppInfoHelperTotalDataTrafficKey;
/**
Monitoring app's properties.
*/
@interface LLAppInfoHelper : NSObject
/**
Singleton to monitoring appinfos.
@return Singleton
*/
+ (instancetype _Nonnull)shared;
/**
Set enable to monitoring network request.
*/
@property (nonatomic, assign, getter=isEnabled) BOOL enable;
/**
Get current app infos. Include "CPU Usage","Memory Usage","FPS","Data Traffic","App Name","Bundle Identifier","App Version","App Start Time","Device Model","Device Name","System Version","Screen Resolution","Language Code","Battery Level","CPU Type","Disk","Network State" and "SSID".
*/
- (NSMutableArray <NSArray <NSDictionary <NSString *,NSString *>*>*>*)appInfos;
/**
Get dynamic app infos this time.
*/
- (NSDictionary <NSString *, NSString *>*)dynamicAppInfos;
/**
Dynamic infos.
*/
- (NSArray <NSDictionary *>*)dynamicInfos;
/**
Application infos.
*/
- (NSArray <NSDictionary *>*)applicationInfos;
/**
Device infos.
*/
- (NSArray <NSDictionary *>*)deviceInfos;
/**
Current cpu usage.
*/
- (NSString *)cpuUsage;
/**
Current memory usage.
*/
- (NSString *)memoryUsage;
/**
Current FPS.
*/
- (NSString *)fps;
/**
Current data traffic.
Format is "{total} ({upload}↑ / {download}↓)"
*/
- (NSString *)dataTraffic;
/**
Application name.
*/
- (NSString *)appName;
/**
Application bundle identifier.
*/
- (NSString *)bundleIdentifier;
/**
Application version.
*/
- (NSString *)appVersion;
/**
Application start time consuming.
*/
- (NSString *)appStartTimeConsuming;
/**
Device model.
*/
- (NSString *)deviceModel;
/**
Device name.
*/
- (NSString *)deviceName;
/**
Device system version.
*/
- (NSString *)systemVersion;
/**
Device screen resolution.
*/
- (NSString *)screenResolution;
/**
Current languageCode.
*/
- (NSString *)languageCode;
/**
Current battery level.
*/
- (NSString *)batteryLevel;
/**
Current cpu type.
*/
- (NSString *)cpuType;
/**
Current disk infos.
*/
- (NSString *)disk;
/**
Current network state.
*/
- (NSString *)networkState;
/**
Current ssid.
*/
- (NSString *_Nullable)ssid;
#pragma mark - PRIMARY (This part of the method is used for internal calls, and users do not actively invoke these interfaces, nor need to care about them.)
/**
Update data traffic when finish a network request.
*/
- (void)updateRequestDataTraffic:(unsigned long long)requestDataTraffic responseDataTraffic:(unsigned long long)responseDataTraffic;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,492 @@
//
// LLAppInfoHelper.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLAppInfoHelper.h"
#import <SystemConfiguration/CaptiveNetwork.h>
#import <malloc/malloc.h>
#import <mach-o/arch.h>
#import <mach/mach.h>
#import "LLInternalMacros.h"
#import "UIDevice+LL_AppInfo.h"
#import "NSObject+LL_Utils.h"
#import "LLRouter+Network.h"
static LLAppInfoHelper *_instance = nil;
NSNotificationName const LLAppInfoHelperDidUpdateAppInfosNotificationName = @"LLAppInfoHelperDidUpdateAppInfosNotificationName";
NSString * const LLAppInfoHelperCPUKey = @"LLAppInfoHelperCPUKey";
NSString * const LLAppInfoHelperMemoryUsedKey = @"LLAppInfoHelperMemoryUsedKey";
NSString * const LLAppInfoHelperMemoryFreeKey = @"LLAppInfoHelperMemoryFreeKey";
NSString * const LLAppInfoHelperMemoryTotalKey = @"LLAppInfoHelperMemoryTotalKey";
NSString * const LLAppInfoHelperFPSKey = @"LLAppInfoHelperFPSKey";
NSString * const LLAppInfoHelperRequestDataTrafficKey = @"LLAppInfoHelperRequestDataTrafficKey";
NSString * const LLAppInfoHelperResponseDataTrafficKey = @"LLAppInfoHelperResponseDataTrafficKey";
NSString * const LLAppInfoHelperTotalDataTrafficKey = @"LLAppInfoHelperTotalDataTrafficKey";
@interface LLAppInfoHelper ()
{
unsigned long long _usedMemory;
unsigned long long _freeMemory;
unsigned long long _totalMemory;
unsigned long long _requestDataTraffic;
unsigned long long _responseDataTraffic;
unsigned long long _totalDataTraffic;
CGFloat _cpu;
CADisplayLink *_link;
NSUInteger _count;
NSTimeInterval _lastTime;
float _fps;
// Cache
NSString *_appName;
NSString *_bundleIdentifier;
NSString *_appVersion;
NSString *_appStartTimeConsuming;
NSString *_deviceModel;
NSString *_deviceName;
NSString *_systemVersion;
NSString *_screenResolution;
NSString *_cpuType;
}
@property (nonatomic, strong) NSTimer *memoryTimer;
@property (nonatomic, copy) NSString *cpuTypeString;
@property (nonatomic, copy) NSString *cpuSubtypeString;
@end
@implementation LLAppInfoHelper
+ (instancetype)shared {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[LLAppInfoHelper alloc] init];
[_instance initial];
});
return _instance;
}
- (void)setEnable:(BOOL)enable {
if (_enable != enable) {
_enable = enable;
if (enable) {
[self startTimers];
} else {
[self removeTimers];
}
}
}
- (NSMutableArray <NSArray <NSDictionary *>*>*)appInfos {
NSArray *dynamic = [self dynamicInfos];
// App Info
NSArray *apps = [self applicationInfos];
// Device Info
NSArray *devices = [self deviceInfos];
return [[NSMutableArray alloc] initWithObjects:dynamic ,apps, devices, nil];
}
- (NSArray <NSDictionary *>*)dynamicInfos {
return @[@{@"CPU" : [self cpuUsage]},
@{@"Memory" : [self memoryUsage]},
@{@"FPS" : [self fps]},
@{@"Data Traffic" : [self dataTraffic]}];
}
- (NSArray <NSDictionary *>*)applicationInfos {
return @[@{@"Name" : [self appName]},
@{@"Identifier" : [self bundleIdentifier]},
@{@"Version" : [self appVersion]},
@{@"Start Time" : [self appStartTimeConsuming]}];
}
- (NSArray <NSDictionary *>*)deviceInfos {
NSArray *devices = @[@{@"Model" : [self deviceModel]},
@{@"Name" : [self deviceName]},
@{@"Version" : [self systemVersion]},
@{@"Resolution" : [self screenResolution]},
@{@"Language" : [self languageCode]},
@{@"Battery" : [self batteryLevel]},
@{@"CPU" : [self cpuType]},
@{@"Disk" : [self disk]},
@{@"Network" : [self networkState]}];
NSMutableArray *mutDevices = [[NSMutableArray alloc] initWithArray:devices];
NSString *ssid = [self ssid];
if (ssid) {
[mutDevices insertObject:@{@"SSID" : ssid} atIndex:7];
}
return mutDevices;
}
- (NSDictionary <NSString *, NSString *>*)dynamicAppInfos {
NSMutableDictionary *infos = [[NSMutableDictionary alloc] init];
for (NSDictionary *dic in [self dynamicInfos]) {
[infos addEntriesFromDictionary:dic];
}
infos[@"Disk"] = [self disk];
infos[@"Network State"] = [self networkState];
return infos;
}
- (void)updateRequestDataTraffic:(unsigned long long)requestDataTraffic responseDataTraffic:(unsigned long long)responseDataTraffic {
if ([[NSThread currentThread] isMainThread]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self updateRequestDataTraffic:requestDataTraffic responseDataTraffic:responseDataTraffic];
});
} else {
@synchronized (self) {
_requestDataTraffic += requestDataTraffic;
_responseDataTraffic += responseDataTraffic;
_totalDataTraffic = _requestDataTraffic + _responseDataTraffic;
}
}
}
- (NSString *)cpuUsage {
return [NSString stringWithFormat:@"%.2f%%",_cpu];
}
- (NSString *)memoryUsage {
NSString *used = [NSByteCountFormatter stringFromByteCount:_usedMemory countStyle:NSByteCountFormatterCountStyleMemory];
NSString *free = [NSByteCountFormatter stringFromByteCount:_freeMemory countStyle:NSByteCountFormatterCountStyleMemory];
return [NSString stringWithFormat:@"Used:%@, Free:%@",used,free];
}
- (NSString *)fps {
return [NSString stringWithFormat:@"%ld FPS",(long)_fps];
}
- (NSString *)dataTraffic {
NSString *total = [NSByteCountFormatter stringFromByteCount:_totalDataTraffic countStyle:NSByteCountFormatterCountStyleFile];
NSString *request = [NSByteCountFormatter stringFromByteCount:_requestDataTraffic countStyle:NSByteCountFormatterCountStyleFile];
NSString *response = [NSByteCountFormatter stringFromByteCount:_responseDataTraffic countStyle:NSByteCountFormatterCountStyleFile];
return [NSString stringWithFormat:@"%@ (%@↑ / %@↓)",total,request,response];
}
- (NSString *)appName {
if (!_appName) {
NSDictionary *infoDic = [NSBundle mainBundle].infoDictionary;
_appName = infoDic[@"CFBundleDisplayName"] ?: infoDic[@"CFBundleName"] ?: @"Unknown";
}
return _appName;
}
- (NSString *)bundleIdentifier {
if (!_bundleIdentifier) {
NSDictionary *infoDic = [NSBundle mainBundle].infoDictionary;
_bundleIdentifier = infoDic[@"CFBundleIdentifier"] ?:@"Unknown";
}
return _bundleIdentifier;
}
- (NSString *)appVersion {
if (!_appVersion) {
NSDictionary *infoDic = [NSBundle mainBundle].infoDictionary;
_appVersion = [NSString stringWithFormat:@"%@(%@)",infoDic[@"CFBundleShortVersionString"]?:@"Unknown",infoDic[@"CFBundleVersion"]?:@"Unknown"];
}
return _appVersion;
}
- (NSString *)appStartTimeConsuming {
if (!_appStartTimeConsuming) {
_appStartTimeConsuming = [NSString stringWithFormat:@"%.2f s",[NSObject LL_startLoadTime]];
}
return _appStartTimeConsuming;
}
- (NSString *)deviceModel {
if (!_deviceModel) {
_deviceModel = [UIDevice currentDevice].LL_modelName ?: @"Unknown";
}
return _deviceModel;
}
- (NSString *)deviceName {
if (!_deviceName) {
_deviceName = [UIDevice currentDevice].name ?: @"Unknown";
}
return _deviceName;
}
- (NSString *)systemVersion {
if (!_systemVersion) {
_systemVersion = [UIDevice currentDevice].systemVersion ?: @"Unknown";
}
return _systemVersion;
}
- (NSString *)screenResolution {
if (!_screenResolution) {
_screenResolution = [NSString stringWithFormat:@"%ld * %ld",(long)(LL_SCREEN_WIDTH * [UIScreen mainScreen].scale),(long)(LL_SCREEN_HEIGHT * [UIScreen mainScreen].scale)];
}
return _screenResolution;
}
- (NSString *)languageCode {
return [NSLocale preferredLanguages].firstObject ?: @"Unknown";
}
- (NSString *)batteryLevel {
return [UIDevice currentDevice].batteryLevel != -1 ? [NSString stringWithFormat:@"%ld%%",(long)([UIDevice currentDevice].batteryLevel * 100)] : @"Unknown";
}
- (NSString *)cpuType {
return [self cpuSubtypeString] ?: @"Unknown";
}
- (NSString *)disk {
NSString *free = [NSByteCountFormatter stringFromByteCount:[self getFreeDisk] countStyle:NSByteCountFormatterCountStyleFile];
NSString *total = [NSByteCountFormatter stringFromByteCount:[self getTotalDisk] countStyle:NSByteCountFormatterCountStyleFile];
return [NSString stringWithFormat:@"%@ / %@", free,total];
}
- (NSString *)networkState {
return [self currentNetworkStatusDescription];
}
- (NSString *)ssid {
return [self currentWifiSSID];
}
#pragma mark - Primary
/**
Init something
*/
- (void)initial {
_fps = 60;
}
- (void)startTimers {
[self removeTimers];
self.memoryTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(memoryTimerAction:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.memoryTimer forMode:NSRunLoopCommonModes];
[self.memoryTimer fire];
_link = [CADisplayLink displayLinkWithTarget:self selector:@selector(fpsDisplayLinkAction:)];
[_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)removeTimers {
if ([self.memoryTimer isValid]) {
[self.memoryTimer invalidate];
self.memoryTimer = nil;
}
if (_link) {
[_link removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[_link invalidate];
_link = nil;
}
}
#pragma mark - CPU
- (float)getCpuUsage
{
kern_return_t kr;
thread_array_t thread_list;
mach_msg_type_number_t thread_count;
thread_info_data_t thinfo;
mach_msg_type_number_t thread_info_count;
thread_basic_info_t basic_info_th;
kr = task_threads(mach_task_self(), &thread_list, &thread_count);
if (kr != KERN_SUCCESS) {
return -1;
}
float cpu_usage = 0;
for (int i = 0; i < thread_count; i++)
{
thread_info_count = THREAD_INFO_MAX;
kr = thread_info(thread_list[i], THREAD_BASIC_INFO,(thread_info_t)thinfo, &thread_info_count);
if (kr != KERN_SUCCESS) {
return -1;
}
basic_info_th = (thread_basic_info_t)thinfo;
if (!(basic_info_th->flags & TH_FLAGS_IDLE))
{
cpu_usage += basic_info_th->cpu_usage;
}
}
cpu_usage = cpu_usage / (float)TH_USAGE_SCALE * 100.0;
vm_deallocate(mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t));
return cpu_usage;
}
- (NSString *)cpuTypeString {
if (!_cpuTypeString) {
_cpuTypeString = [self stringFromCpuType:[self getCpuType]];
}
return _cpuTypeString;
}
- (NSString *)cpuSubtypeString {
if (!_cpuSubtypeString) {
_cpuSubtypeString = [NSString stringWithUTF8String:NXGetLocalArchInfo()->description];
}
return _cpuSubtypeString;
}
- (NSInteger)getCpuType {
return (NSInteger)(NXGetLocalArchInfo()->cputype);
}
- (NSInteger)getCpuSubtype {
return (NSInteger)(NXGetLocalArchInfo()->cpusubtype);
}
- (NSString *)stringFromCpuType:(NSInteger)cpuType {
switch (cpuType) {
case CPU_TYPE_VAX: return @"VAX";
case CPU_TYPE_MC680x0: return @"MC680x0";
case CPU_TYPE_X86: return @"X86";
case CPU_TYPE_X86_64: return @"X86_64";
case CPU_TYPE_MC98000: return @"MC98000";
case CPU_TYPE_HPPA: return @"HPPA";
case CPU_TYPE_ARM: return @"ARM";
case CPU_TYPE_ARM64: return @"ARM64";
case CPU_TYPE_MC88000: return @"MC88000";
case CPU_TYPE_SPARC: return @"SPARC";
case CPU_TYPE_I860: return @"I860";
case CPU_TYPE_POWERPC: return @"POWERPC";
case CPU_TYPE_POWERPC64: return @"POWERPC64";
default: return @"Unknown";
}
}
#pragma mark - Memory
- (void)memoryTimerAction:(NSTimer *)timer {
struct mstats stat = mstats();
_usedMemory = stat.bytes_used;
_freeMemory = stat.bytes_free;
_totalMemory = stat.bytes_total;
_cpu = [self getCpuUsage];
[self postAppInfoHelperDidUpdateAppInfosNotification];
}
- (void)postAppInfoHelperDidUpdateAppInfosNotification {
if ([[NSThread currentThread] isMainThread]) {
[[NSNotificationCenter defaultCenter] postNotificationName:LLAppInfoHelperDidUpdateAppInfosNotificationName object:[self dynamicInfos] userInfo:@{LLAppInfoHelperCPUKey:@(_cpu),LLAppInfoHelperFPSKey:@(_fps),LLAppInfoHelperMemoryFreeKey:@(_freeMemory),LLAppInfoHelperMemoryUsedKey:@(_usedMemory),LLAppInfoHelperMemoryTotalKey:@(_totalMemory),LLAppInfoHelperRequestDataTrafficKey:@(_requestDataTraffic),LLAppInfoHelperResponseDataTrafficKey:@(_responseDataTraffic),LLAppInfoHelperTotalDataTrafficKey:@(_totalDataTraffic)}];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
[self postAppInfoHelperDidUpdateAppInfosNotification];
});
}
}
#pragma mark - FPS
- (void)fpsDisplayLinkAction:(CADisplayLink *)link {
if (_lastTime == 0) {
_lastTime = link.timestamp;
return;
}
_count++;
NSTimeInterval delta = link.timestamp - _lastTime;
if (delta < 1) return;
_lastTime = link.timestamp;
_fps = _count / delta;
_count = 0;
}
#pragma mark - Disk
- (unsigned long long)getTotalDisk {
NSDictionary *fattributes = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil];
return [[fattributes objectForKey:NSFileSystemSize] unsignedLongLongValue];
}
- (unsigned long long)getFreeDisk {
NSDictionary *fattributes = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:nil];
return [[fattributes objectForKey:NSFileSystemFreeSize] unsignedLongLongValue];
}
#pragma mark - Network
- (NSString *)currentWifiSSID
{
NSString *ssid = nil;
CFArrayRef ifRef = CNCopySupportedInterfaces();
NSArray *ifs = (__bridge id)ifRef;
for (NSString *ifname in ifs) {
CFDictionaryRef dictionaryRef = CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifname);
NSDictionary *info = (__bridge id)dictionaryRef;
if (info[@"SSIDD"])
{
ssid = info[@"SSID"];
}
if (dictionaryRef) {
CFAutorelease(dictionaryRef);
}
}
if (ifRef) {
CFAutorelease(ifRef);
}
return ssid;
}
- (NSString *)currentNetworkStatusDescription {
NSString *returnValue = @"Unknown";
switch ([LLRouter networkStateFromStatebar]) {
case LLNetworkStatusNotReachable:{
returnValue = @"Unknown";
}
break;
case LLNetworkStatusReachableViaWWAN: {
returnValue = @"WWAN";
}
break;
case LLNetworkStatusReachableViaWWAN2G:{
returnValue = @"WWAN 2G";
}
break;
case LLNetworkStatusReachableViaWWAN3G:{
returnValue = @"WWAN 3G";
}
break;
case LLNetworkStatusReachableViaWWAN4G:{
returnValue = @"WWAN 4G";
}
break;
case LLNetworkStatusReachableViaWiFi: {
returnValue = @"WiFi";
}
}
return returnValue;
}
@end

View File

@@ -0,0 +1,37 @@
//
// UIDevice+LL_AppInfo.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/// UIDevice utils.
@interface UIDevice (LL_AppInfo)
/**
* Change platform name to model name, such as change "iPhone10,6" to "iPhone X".
*/
@property (nonatomic, copy, readonly) NSString *LL_modelName;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,168 @@
//
// UIDevice+LL_AppInfo.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "UIDevice+LL_AppInfo.h"
#import <sys/sysctl.h>
#import "NSObject+LL_Runtime.h"
static const char kLLModelNameKey;
@implementation UIDevice (LL_AppInfo)
- (NSString *)LL_modelName {
NSString *name = [self LL_getStringProperty:&kLLModelNameKey];
if (name == nil) {
name = [self LL_getCurrentDeviceModel];
[self LL_setStringProperty:name key:&kLLModelNameKey];
}
return name;
}
#pragma mark - Primary
- (NSString *)LL_getCurrentDeviceModel
{
NSString *platform = [self LL_platform];
if ([platform hasPrefix:@"iPhone"]) {
if ([platform isEqualToString:@"iPhone12,5"]) return @"iPhone 11 Pro Max";
if ([platform isEqualToString:@"iPhone12,3"]) return @"iPhone 11 Pro";
if ([platform isEqualToString:@"iPhone12,1"]) return @"iPhone 11";
if ([platform isEqualToString:@"iPhone11,8"]) return @"iPhone XR";
if ([platform isEqualToString:@"iPhone11,6"]) return @"iPhone XS Max";
if ([platform isEqualToString:@"iPhone11,4"]) return @"iPhone XS Max";
if ([platform isEqualToString:@"iPhone11,2"]) return @"iPhone XS";
if ([platform isEqualToString:@"iPhone10,6"]) return @"iPhone X";
if ([platform isEqualToString:@"iPhone10,5"]) return @"iPhone 8 Plus";
if ([platform isEqualToString:@"iPhone10,4"]) return @"iPhone 8";
if ([platform isEqualToString:@"iPhone10,3"]) return @"iPhone X";
if ([platform isEqualToString:@"iPhone10,2"]) return @"iPhone 8 Plus";
if ([platform isEqualToString:@"iPhone10,1"]) return @"iPhone 8";
if ([platform isEqualToString:@"iPhone9,4"]) return @"iPhone 7 Plus";
if ([platform isEqualToString:@"iPhone9,3"]) return @"iPhone 7";
if ([platform isEqualToString:@"iPhone9,2"]) return @"iPhone 7 Plus";
if ([platform isEqualToString:@"iPhone9,1"]) return @"iPhone 7";
if ([platform isEqualToString:@"iPhone8,4"]) return @"iPhone SE";
if ([platform isEqualToString:@"iPhone8,2"]) return @"iPhone 6s Plus";
if ([platform isEqualToString:@"iPhone8,1"]) return @"iPhone 6s";
if ([platform isEqualToString:@"iPhone7,2"]) return @"iPhone 6";
if ([platform isEqualToString:@"iPhone7,1"]) return @"iPhone 6 Plus";
if ([platform isEqualToString:@"iPhone6,2"]) return @"iPhone 5s";
if ([platform isEqualToString:@"iPhone6,1"]) return @"iPhone 5s";
if ([platform isEqualToString:@"iPhone5,4"]) return @"iPhone 5c";
if ([platform isEqualToString:@"iPhone5,3"]) return @"iPhone 5c";
if ([platform isEqualToString:@"iPhone5,2"]) return @"iPhone 5";
if ([platform isEqualToString:@"iPhone5,1"]) return @"iPhone 5";
if ([platform isEqualToString:@"iPhone4,1"]) return @"iPhone 4S";
if ([platform isEqualToString:@"iPhone3,3"]) return @"iPhone 4";
if ([platform isEqualToString:@"iPhone3,2"]) return @"iPhone 4";
if ([platform isEqualToString:@"iPhone3,1"]) return @"iPhone 4";
if ([platform isEqualToString:@"iPhone2,1"]) return @"iPhone 3GS";
if ([platform isEqualToString:@"iPhone1,2"]) return @"iPhone 3G";
if ([platform isEqualToString:@"iPhone1,1"]) return @"iPhone 1G";
return @"iPhone";
} else if ([platform hasPrefix:@"iPad"]) {
if ([platform isEqualToString:@"iPad11,4"]) return @"iPad Air 3";
if ([platform isEqualToString:@"iPad11,3"]) return @"iPad Air 3";
if ([platform isEqualToString:@"iPad11,2"]) return @"iPad Mini 5";
if ([platform isEqualToString:@"iPad11,1"]) return @"iPad Mini 5";
if ([platform isEqualToString:@"iPad8,8"]) return @"iPad Pro 3";
if ([platform isEqualToString:@"iPad8,7"]) return @"iPad Pro 3";
if ([platform isEqualToString:@"iPad8,6"]) return @"iPad Pro 3";
if ([platform isEqualToString:@"iPad8,5"]) return @"iPad Pro 3";
if ([platform isEqualToString:@"iPad8,4"]) return @"iPad Pro 3";
if ([platform isEqualToString:@"iPad8,3"]) return @"iPad Pro 3";
if ([platform isEqualToString:@"iPad8,2"]) return @"iPad Pro 3";
if ([platform isEqualToString:@"iPad8,1"]) return @"iPad Pro 3";
if ([platform isEqualToString:@"iPad7,6"]) return @"iPad 6";
if ([platform isEqualToString:@"iPad7,5"]) return @"iPad 6";
if ([platform isEqualToString:@"iPad7,4"]) return @"iPad Pro";
if ([platform isEqualToString:@"iPad7,3"]) return @"iPad Pro";
if ([platform isEqualToString:@"iPad7,2"]) return @"iPad Pro 2";
if ([platform isEqualToString:@"iPad7,1"]) return @"iPad Pro 2";
if ([platform isEqualToString:@"iPad6,12"]) return @"iPad 5";
if ([platform isEqualToString:@"iPad6,11"]) return @"iPad 5";
if ([platform isEqualToString:@"iPad6,8"]) return @"iPad Pro";
if ([platform isEqualToString:@"iPad6,7"]) return @"iPad Pro";
if ([platform isEqualToString:@"iPad6,4"]) return @"iPad Pro";
if ([platform isEqualToString:@"iPad6,3"]) return @"iPad Pro";
if ([platform isEqualToString:@"iPad5,4"]) return @"iPad Air 2";
if ([platform isEqualToString:@"iPad5,3"]) return @"iPad Air 2";
if ([platform isEqualToString:@"iPad5,2"]) return @"iPad Mini 4";
if ([platform isEqualToString:@"iPad5,1"]) return @"iPad Mini 4";
if ([platform isEqualToString:@"iPad4,9"]) return @"iPad Mini 3";
if ([platform isEqualToString:@"iPad4,8"]) return @"iPad Mini 3";
if ([platform isEqualToString:@"iPad4,7"]) return @"iPad Mini 3";
if ([platform isEqualToString:@"iPad4,6"]) return @"iPad Mini 2";
if ([platform isEqualToString:@"iPad4,5"]) return @"iPad Mini 2";
if ([platform isEqualToString:@"iPad4,4"]) return @"iPad Mini 2";
if ([platform isEqualToString:@"iPad4,3"]) return @"iPad Air";
if ([platform isEqualToString:@"iPad4,2"]) return @"iPad Air";
if ([platform isEqualToString:@"iPad4,1"]) return @"iPad Air";
if ([platform isEqualToString:@"iPad3,6"]) return @"iPad 4";
if ([platform isEqualToString:@"iPad3,5"]) return @"iPad 4";
if ([platform isEqualToString:@"iPad3,4"]) return @"iPad 4";
if ([platform isEqualToString:@"iPad3,3"]) return @"iPad 3";
if ([platform isEqualToString:@"iPad3,2"]) return @"iPad 3";
if ([platform isEqualToString:@"iPad3,1"]) return @"iPad 3";
if ([platform isEqualToString:@"iPad2,7"]) return @"iPad Mini";
if ([platform isEqualToString:@"iPad2,6"]) return @"iPad Mini";
if ([platform isEqualToString:@"iPad2,5"]) return @"iPad Mini";
if ([platform isEqualToString:@"iPad2,4"]) return @"iPad 2";
if ([platform isEqualToString:@"iPad2,3"]) return @"iPad 2";
if ([platform isEqualToString:@"iPad2,2"]) return @"iPad 2";
if ([platform isEqualToString:@"iPad2,1"]) return @"iPad 2";
if ([platform isEqualToString:@"iPad1,1"]) return @"iPad 1";
return @"iPad";
} else if ([platform hasPrefix:@"iPod"]) {
if ([platform isEqualToString:@"iPod9,1"]) return @"iPod 7";
if ([platform isEqualToString:@"iPod7,1"]) return @"iPod 6";
if ([platform isEqualToString:@"iPod5,1"]) return @"iPod 5";
if ([platform isEqualToString:@"iPod4,1"]) return @"iPod 4";
if ([platform isEqualToString:@"iPod3,1"]) return @"iPod 3";
if ([platform isEqualToString:@"iPod2,1"]) return @"iPod 2";
if ([platform isEqualToString:@"iPod1,1"]) return @"iPod 1";
return @"iPod";
} else {
if ([platform isEqualToString:@"i386"]) return @"iPhone Simulator (i386)";
if ([platform isEqualToString:@"x86_64"]) return @"iPhone Simulator (x86_64)";
}
return @"unknown";
}
- (NSString *)LL_platform
{
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
char *machine = malloc(size);
sysctlbyname("hw.machine", machine, &size, NULL, 0);
NSString *platform = [NSString stringWithUTF8String:machine];
free(machine);
return platform;
}
@end

View File

@@ -0,0 +1,38 @@
//
// LLAppInfo.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef LLAppInfo_h
#define LLAppInfo_h
#import "LLAppInfoComponent.h"
#import "LLAppInfoHelper.h"
#import "UIDevice+LL_AppInfo.h"
#import "LLAppInfoViewController.h"
#import "LLAppInfoWindow.h"
#import "LLWindowManager+AppInfo.h"
#endif /* LLAppInfo_h */

View File

@@ -0,0 +1,33 @@
//
// LLAppInfoComponent.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLComponent.h"
NS_ASSUME_NONNULL_BEGIN
/// App info function component.
@interface LLAppInfoComponent : LLComponent
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,43 @@
//
// LLAppInfoComponent.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLAppInfoComponent.h"
#import "LLAppInfoViewController.h"
#import "LLNavigationController.h"
#import "LLWindowManager+AppInfo.h"
@implementation LLAppInfoComponent
- (void)componentDidLoad:(NSDictionary *)data {
LLBaseWindow *window = [[LLWindowManager shared] visiableWindow];
if ([window isKindOfClass:[LLFunctionWindow class]]) {
LLNavigationController *nav = (LLNavigationController *)window.rootViewController;
[nav pushViewController:[[LLAppInfoViewController alloc] init] animated:YES];
} else {
[[LLWindowManager shared] showWindow:[LLWindowManager appInfoWindow] animated:YES];
}
}
@end

View File

@@ -0,0 +1,33 @@
//
// LLAppInfoViewController.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLTitleViewController.h"
NS_ASSUME_NONNULL_BEGIN
/// App info function view controller.
@interface LLAppInfoViewController : LLTitleViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,121 @@
//
// LLAppInfoViewController.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLAppInfoViewController.h"
#import "LLTitleCellCategoryModel.h"
#import "LLBaseTableViewCell.h"
#import "LLInternalMacros.h"
#import "LLTitleCellModel.h"
#import "LLAppInfoHelper.h"
#import "LLThemeManager.h"
#import "LLFactory.h"
#import "LLConfig.h"
@interface LLAppInfoViewController ()
@end
@implementation LLAppInfoViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = [[LLAppInfoHelper shared] deviceName] ?: LLLocalizedString(@"function.app.info");
[self loadData];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self registerLLAppInfoHelperNotification];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self unregisterLLAppInfoHelperNotification];
}
#pragma mark - LLAppInfoHelperNotification
- (void)registerLLAppInfoHelperNotification {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveLLAppInfoHelperDidUpdateAppInfosNotification:) name:LLAppInfoHelperDidUpdateAppInfosNotificationName object:nil];
}
- (void)unregisterLLAppInfoHelperNotification {
[[NSNotificationCenter defaultCenter] removeObserver:self name:LLAppInfoHelperDidUpdateAppInfosNotificationName object:nil];
}
- (void)didReceiveLLAppInfoHelperDidUpdateAppInfosNotification:(NSNotification *)notification {
[self updateDynamicData];
}
#pragma mark - Primary
- (void)loadData {
[self.dataArray removeAllObjects];
[self.dataArray addObject:[self dynamicData]];
[self.dataArray addObject:[self applicationData]];
[self.dataArray addObject:[self deviceData]];
[self.tableView reloadData];
}
- (void)updateDynamicData {
if (self.dataArray.count > 1) {
[self.dataArray replaceObjectAtIndex:0 withObject:[self dynamicData]];
[self.tableView reloadData];
}
}
- (LLTitleCellCategoryModel *)dynamicData {
NSMutableArray *settings = [[NSMutableArray alloc] init];
for (NSDictionary *dic in [[LLAppInfoHelper shared] dynamicInfos]) {
LLTitleCellModel *model = [[LLTitleCellModel alloc] initWithTitle:dic.allKeys.firstObject detailTitle:dic.allValues.firstObject];
[settings addObject:model];
}
return [[LLTitleCellCategoryModel alloc] initWithTitle:LLLocalizedString(@"app.info.dynamic") items:settings];
}
- (LLTitleCellCategoryModel *)applicationData {
NSMutableArray *settings = [[NSMutableArray alloc] init];
for (NSDictionary *dic in [[LLAppInfoHelper shared] applicationInfos]) {
LLTitleCellModel *model = [[LLTitleCellModel alloc] initWithTitle:dic.allKeys.firstObject detailTitle:dic.allValues.firstObject];
[settings addObject:model];
}
return [[LLTitleCellCategoryModel alloc] initWithTitle:LLLocalizedString(@"app.info.application") items:settings];
}
- (LLTitleCellCategoryModel *)deviceData {
NSMutableArray *settings = [[NSMutableArray alloc] init];
for (NSDictionary *dic in [[LLAppInfoHelper shared] deviceInfos]) {
LLTitleCellModel *model = [[LLTitleCellModel alloc] initWithTitle:dic.allKeys.firstObject detailTitle:dic.allValues.firstObject];
[settings addObject:model];
}
return [[LLTitleCellCategoryModel alloc] initWithTitle:LLLocalizedString(@"app.info.device") items:settings];
}
@end

View File

@@ -0,0 +1,33 @@
//
// LLAppInfoWindow.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLComponentWindow.h"
NS_ASSUME_NONNULL_BEGIN
/// App info function window.
@interface LLAppInfoWindow : LLComponentWindow
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,47 @@
//
// LLAppInfoWindow.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLAppInfoWindow.h"
#import "LLAppInfoViewController.h"
#import "LLNavigationController.h"
#import "LLWindowManager.h"
@implementation LLAppInfoWindow
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.showAnimateStyle = LLBaseWindowShowAnimateStylePresent;
self.hideAnimateStyle = LLBaseWindowHideAnimateStyleDismiss;
if (!self.rootViewController) {
self.rootViewController = [[LLNavigationController alloc] initWithRootViewController:[[LLAppInfoViewController alloc] init]];
}
}
return self;
}
- (void)componentDidFinish {
[[LLWindowManager shared] showEntryWindow];
}
@end

View File

@@ -0,0 +1,41 @@
//
// LLWindowManager+AppInfo.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLWindowManager.h"
#import "LLAppInfoWindow.h"
NS_ASSUME_NONNULL_BEGIN
@interface LLWindowManager (AppInfo)
/**
AppInfo window
@return LLAppInfoWindow.
*/
+ (LLAppInfoWindow *)appInfoWindow;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,32 @@
//
// LLWindowManager+AppInfo.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLWindowManager+AppInfo.h"
@implementation LLWindowManager (AppInfo)
+ (LLAppInfoWindow *)appInfoWindow {
return (LLAppInfoWindow *)[self createWindowWithClassName:NSStringFromClass([LLAppInfoWindow class])];
}
@end

View File

@@ -0,0 +1,49 @@
//
// LLCrashHelper.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <Foundation/Foundation.h>
@class LLCrashModel;
NS_ASSUME_NONNULL_BEGIN
/**
Catch crash or not.
*/
@interface LLCrashHelper : NSObject
/**
Singleton to control enable.
@return Singleton
*/
+ (instancetype _Nonnull)shared;
/**
Set enable to catch crash.
*/
@property (nonatomic, assign, getter=isEnabled) BOOL enable;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,328 @@
//
// LLCrashHelper.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLCrashHelper.h"
#include <libkern/OSAtomic.h>
#include <execinfo.h>
#import "LLStorageManager.h"
#import "LLFormatterTool.h"
#import "LLCrashModel.h"
#import "LLConfig.h"
#import "LLTool.h"
#import "NSObject+LL_Utils.h"
#import "LLRouter+AppInfo.h"
static LLCrashHelper *_instance = nil;
@interface LLCrashHelper ()
@end
@implementation LLCrashHelper
+ (instancetype)shared {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[LLCrashHelper alloc] init];
});
return _instance;
}
- (void)setEnable:(BOOL)enable {
if (_enable != enable) {
_enable = enable;
if (enable) {
[self registerCatch];
} else {
[self unregisterCatch];
}
}
}
#pragma mark - Primary
- (void)registerCatch {
NSSetUncaughtExceptionHandler(&HandleException);
signal(SIGHUP, SignalHandler);
signal(SIGINT, SignalHandler);
signal(SIGQUIT, SignalHandler);
signal(SIGILL, SignalHandler);
signal(SIGTRAP, SignalHandler);
signal(SIGABRT, SignalHandler);
#ifdef SIGPOLL
signal(SIGPOLL, SignalHandler);
#endif
#ifdef SIGEMT
signal(SIGEMT, SignalHandler);
#endif
signal(SIGFPE, SignalHandler);
signal(SIGKILL, SignalHandler);
signal(SIGBUS, SignalHandler);
signal(SIGSEGV, SignalHandler);
signal(SIGSYS, SignalHandler);
signal(SIGPIPE, SignalHandler);
signal(SIGALRM, SignalHandler);
signal(SIGTERM, SignalHandler);
signal(SIGURG, SignalHandler);
signal(SIGSTOP, SignalHandler);
signal(SIGTSTP, SignalHandler);
signal(SIGCONT, SignalHandler);
signal(SIGCHLD, SignalHandler);
signal(SIGTTIN, SignalHandler);
signal(SIGTTOU, SignalHandler);
#ifdef SIGIO
signal(SIGIO, SignalHandler);
#endif
signal(SIGXCPU, SignalHandler);
signal(SIGXFSZ, SignalHandler);
signal(SIGVTALRM, SignalHandler);
signal(SIGPROF, SignalHandler);
#ifdef SIGWINCH
signal(SIGWINCH, SignalHandler);
#endif
#ifdef SIGINFO
signal(SIGINFO, SignalHandler);
#endif
signal(SIGUSR1, SignalHandler);
signal(SIGUSR2, SignalHandler);
}
- (void)unregisterCatch {
NSSetUncaughtExceptionHandler(nil);
signal(SIGHUP, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGQUIT, SIG_DFL);
signal(SIGILL, SIG_DFL);
signal(SIGTRAP, SIG_DFL);
signal(SIGABRT, SIG_DFL);
#ifdef SIGPOLL
signal(SIGPOLL, SIG_DFL);
#endif
#ifdef SIGEMT
signal(SIGEMT, SIG_DFL);
#endif
signal(SIGFPE, SIG_DFL);
signal(SIGKILL, SIG_DFL);
signal(SIGBUS, SIG_DFL);
signal(SIGSEGV, SIG_DFL);
signal(SIGSYS, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
signal(SIGALRM, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGURG, SIG_DFL);
signal(SIGSTOP, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
signal(SIGCONT, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
signal(SIGTTIN, SIG_DFL);
signal(SIGTTOU, SIG_DFL);
#ifdef SIGIO
signal(SIGIO, SIG_DFL);
#endif
signal(SIGXCPU, SIG_DFL);
signal(SIGXFSZ, SIG_DFL);
signal(SIGVTALRM, SIG_DFL);
signal(SIGPROF, SIG_DFL);
#ifdef SIGWINCH
signal(SIGWINCH, SIG_DFL);
#endif
#ifdef SIGINFO
signal(SIGINFO, SIG_DFL);
#endif
signal(SIGUSR1, SIG_DFL);
signal(SIGUSR2, SIG_DFL);
}
- (void)saveException:(NSException *)exception {
NSString *date = [LLFormatterTool stringFromDate:[NSDate date] style:FormatterToolDateStyle1];
NSArray *appInfos = [LLRouter appInfos];
LLCrashModel *model = [[LLCrashModel alloc] initWithName:exception.name reason:exception.reason userInfo:exception.userInfo stackSymbols:exception.callStackSymbols date:date thread:[NSThread currentThread].description userIdentity:[LLConfig shared].userIdentity appInfos:appInfos launchDate:[NSObject LL_launchDate]];
[[LLStorageManager shared] saveModel:model complete:^(BOOL result) {
[LLTool log:@"Save crash model success"];
} synchronous:YES];
[LLTool log:[NSString stringWithFormat:@"%@",exception] synchronous:YES withPrompt:NO];
}
void HandleException(NSException *exception)
{
[[LLCrashHelper shared] saveException:exception];
[exception raise];
}
void SignalHandler(int sig)
{
// See https://stackoverflow.com/questions/40631334/how-to-intercept-exc-bad-instruction-when-unwrapping-nil.
NSString *name = @"Unknown signal";
switch (sig) {
case SIGHUP:{
name = @"SIGHUP";
}
break;
case SIGINT:{
name = @"SIGINT";
}
break;
case SIGQUIT:{
name = @"SIGQUIT";
}
break;
case SIGILL:{
name = @"SIGILL";
}
break;
case SIGTRAP:{
name = @"SIGTRAP";
}
break;
case SIGABRT:{
name = @"SIGABRT";
}
break;
#ifdef SIGPOLL
case SIGPOLL:{
name = @"SIGPOLL";
}
break;
#endif
case SIGEMT:{
name = @"SIGEMT";
}
break;
case SIGFPE:{
name = @"SIGFPE";
}
break;
case SIGKILL:{
name = @"SIGKILL";
}
break;
case SIGBUS:{
name = @"SIGBUS";
}
break;
case SIGSEGV:{
name = @"SIGSEGV";
}
break;
case SIGSYS:{
name = @"SIGSYS";
}
break;
case SIGPIPE:{
name = @"SIGPIPE";
}
break;
case SIGALRM:{
name = @"SIGALRM";
}
break;
case SIGTERM:{
name = @"SIGTERM";
}
break;
case SIGURG:{
name = @"SIGURG";
}
break;
case SIGSTOP:{
name = @"SIGSTOP";
}
break;
case SIGTSTP:{
name = @"SIGTSTP";
}
break;
case SIGCONT:{
name = @"SIGCONT";
}
break;
case SIGCHLD:{
name = @"SIGCHLD";
}
break;
case SIGTTIN:{
name = @"SIGTTIN";
}
break;
case SIGTTOU:{
name = @"SIGTTOU";
}
break;
#ifdef SIGIO
case SIGIO:{
name = @"SIGIO";
}
break;
#endif
case SIGXCPU:{
name = @"SIGXCPU";
}
break;
case SIGXFSZ:{
name = @"SIGXFSZ";
}
break;
case SIGVTALRM:{
name = @"SIGVTALRM";
}
break;
case SIGPROF:{
name = @"SIGPROF";
}
break;
#ifdef SIGWINCH
case SIGWINCH:{
name = @"SIGWINCH";
}
break;
#endif
#ifdef SIGINFO
case SIGINFO:{
name = @"SIGINFO";
}
break;
#endif
case SIGUSR1:{
name = @"SIGUSR1";
}
break;
case SIGUSR2:{
name = @"SIGUSR2";
}
break;
}
NSArray *callStackSymbols = [NSThread callStackSymbols];
NSString *date = [LLFormatterTool stringFromDate:[NSDate date] style:FormatterToolDateStyle1];
LLCrashModel *model = [[LLCrashModel alloc] initWithName:name reason:[NSString stringWithFormat:@"%@ Signal",name] userInfo:nil stackSymbols:callStackSymbols date:date thread:[NSThread currentThread].description userIdentity:[LLConfig shared].userIdentity appInfos:[LLRouter appInfos] launchDate:[NSObject LL_launchDate]];
[[LLStorageManager shared] saveModel:model complete:^(BOOL result) {
[LLTool log:@"Save signal model success"];
} synchronous:YES];
kill(getpid(), SIGKILL);
}
@end

View File

@@ -0,0 +1,95 @@
//
// LLCrashModel.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLTableViewSelectableModel.h"
@class LLCrashSignalModel;
NS_ASSUME_NONNULL_BEGIN
/// Crash model.
@interface LLCrashModel : LLTableViewSelectableModel
/**
* Crash Name
*/
@property (copy, nonatomic, readonly, nullable) NSString *name;
/**
* Crash reason
*/
@property (copy, nonatomic, readonly, nullable) NSString *reason;
/**
* Crash UserInfo
*/
@property (strong, nonatomic, readonly, nullable) NSDictionary <NSString *,id>*userInfo;
/**
* Crash stack symbols
*/
@property (strong, nonatomic, readonly, nullable) NSArray <NSString *>*stackSymbols;
/**
* Crash Date (yyyy-MM-dd HH:mm:ss)
*/
@property (copy, nonatomic, readonly, nullable) NSString *date;
/**
* Thread description.
*/
@property (copy, nonatomic, readonly, nullable) NSString *thread;
/**
* Custom User Identity
*/
@property (copy, nonatomic, readonly, nullable) NSString *userIdentity;
/**
* App Infos
*/
@property (strong, nonatomic, readonly, nullable) NSArray <NSArray <NSDictionary <NSString *,NSString *>*>*>*appInfos;
/**
* App LaunchDate
*/
@property (copy, nonatomic, readonly) NSString *launchDate;
/**
Model identity.
*/
@property (nonatomic, copy, readonly) NSString *identity;
/**
* Init method
*/
- (instancetype _Nonnull)initWithName:(NSString *_Nullable)name reason:(NSString *_Nullable)reason userInfo:(NSDictionary <NSString *, id>*_Nullable)userInfo stackSymbols:(NSArray <NSString *>*_Nullable)stackSymbols date:(NSString *_Nullable)date thread:(NSString *_Nullable)thread userIdentity:(NSString *_Nullable)userIdentity appInfos:(NSArray <NSArray <NSDictionary <NSString *,NSString *>*>*>*_Nullable)appInfos launchDate:(NSString *)launchDate;
/**
Update appInfo
*/
- (void)updateAppInfos:(NSArray <NSArray <NSDictionary <NSString *,NSString *>*>*>*_Nullable)appInfos;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,74 @@
//
// LLCrashModel.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLCrashModel.h"
#import "LLTool.h"
#import "NSDictionary+LL_Utils.h"
#import "NSObject+LL_Utils.h"
#import "NSString+LL_Utils.h"
#import "NSArray+LL_Utils.h"
@interface LLCrashModel ()
@property (strong, nonatomic) NSMutableArray <LLCrashSignalModel *>*signals;
@end
@implementation LLCrashModel
- (instancetype _Nonnull)initWithName:(NSString *_Nullable)name reason:(NSString *_Nullable)reason userInfo:(NSDictionary <NSString *, id>*_Nullable)userInfo stackSymbols:(NSArray <NSString *>*_Nullable)stackSymbols date:(NSString *_Nullable)date thread:(NSString *_Nullable)thread userIdentity:(NSString *_Nullable)userIdentity appInfos:(NSArray <NSArray <NSDictionary <NSString *,NSString *>*>*>*_Nullable)appInfos launchDate:(NSString *)launchDate {
if (self = [super init]) {
_name = [name copy];
_reason = [reason copy];
_userInfo = [userInfo copy];
_stackSymbols = [stackSymbols copy];
_date = [date copy];
_thread = [thread copy];
_userIdentity = [userIdentity copy];
_appInfos = [appInfos copy];
_launchDate = [launchDate copy];
_signals = [[NSMutableArray alloc] init];
_identity = [launchDate stringByAppendingString:[LLTool absolutelyIdentity]];
}
return self;
}
- (void)updateAppInfos:(NSArray <NSArray <NSDictionary <NSString *,NSString *>*>*>*)appInfos {
_appInfos = [appInfos copy];
}
- (NSString *)storageIdentity {
return self.identity;
}
- (BOOL)operationOnMainThread {
return YES;
}
- (NSString *)description {
return [NSString stringWithFormat:@"[LLCrashModel] \n name:%@,\n reason:%@,\n userInfo:%@,\n stackSymbols:%@,\n date:%@,\n userIdentity:%@,\n appInfos:%@,\n launchDate:%@",self.name,self.reason,self.userInfo.LL_jsonString,self.stackSymbols.LL_jsonString,self.date,self.userIdentity,self.appInfos.LL_jsonString,self.launchDate];
}
@end

View File

@@ -0,0 +1,39 @@
//
// LLCrash.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef LLCrash_h
#define LLCrash_h
#import "LLCrashComponent.h"
#import "LLCrashHelper.h"
#import "LLCrashModel.h"
#import "LLCrashDetailViewController.h"
#import "LLCrashViewController.h"
#import "LLCrashWindow.h"
#import "LLCrashCell.h"
#import "LLWindowManager+Crash.h"
#endif /* LLCrash_h */

View File

@@ -0,0 +1,33 @@
//
// LLCrashComponent.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLComponent.h"
NS_ASSUME_NONNULL_BEGIN
/// Crash function component.
@interface LLCrashComponent : LLComponent
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,43 @@
//
// LLCrashComponent.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLCrashComponent.h"
#import "LLNavigationController.h"
#import "LLCrashViewController.h"
#import "LLWindowManager+Crash.h"
@implementation LLCrashComponent
- (void)componentDidLoad:(NSDictionary *)data {
LLBaseWindow *window = [[LLWindowManager shared] visiableWindow];
if ([window isKindOfClass:[LLFunctionWindow class]]) {
LLNavigationController *nav = (LLNavigationController *)window.rootViewController;
[nav pushViewController:[[LLCrashViewController alloc] init] animated:YES];
} else {
[[LLWindowManager shared] showWindow:[LLWindowManager crashWindow] animated:YES];
}
}
@end

View File

@@ -0,0 +1,39 @@
//
// LLCrashCell.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLBaseTableViewCell.h"
@class LLCrashModel;
NS_ASSUME_NONNULL_BEGIN
/// Crash function cell.
@interface LLCrashCell : LLBaseTableViewCell
/// Config cell with model.
/// @param model LLCrashModel.
- (void)confirmWithModel:(LLCrashModel *)model;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,122 @@
//
// LLCrashCell.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLCrashCell.h"
#import "LLCrashModel.h"
#import "LLFactory.h"
#import "LLConfig.h"
#import "LLConst.h"
@interface LLCrashCell ()
@property (nonatomic, strong) UILabel *reasonLabel;
@property (nonatomic, strong) UILabel *nameLabel;
@property (nonatomic, strong) UILabel *dateLabel;
@property (nonatomic, strong) LLCrashModel *model;
@end
@implementation LLCrashCell
- (void)confirmWithModel:(LLCrashModel *)model {
_model = model;
_nameLabel.text = model.name;
_reasonLabel.text = model.reason;
_dateLabel.text = [NSString stringWithFormat:@"[ %@ ]",model.date];
}
#pragma mark - Over write
- (void)initUI {
[super initUI];
self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[self.contentView addSubview:self.reasonLabel];
[self.contentView addSubview:self.nameLabel];
[self.contentView addSubview:self.dateLabel];
[self addReasonLabelConstraints];
[self addNameLabelConstraints];
[self addDateLabelConstraints];
}
- (void)addReasonLabelConstraints {
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:self.reasonLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.reasonLabel.superview attribute:NSLayoutAttributeTop multiplier:1 constant:kLLGeneralMargin];
NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:self.reasonLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.reasonLabel.superview attribute:NSLayoutAttributeLeading multiplier:1 constant:kLLGeneralMargin];
NSLayoutConstraint *right = [NSLayoutConstraint constraintWithItem:self.reasonLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.reasonLabel.superview attribute:NSLayoutAttributeTrailing multiplier:1 constant:-kLLGeneralMargin];
self.reasonLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.reasonLabel.superview addConstraints:@[top, left, right]];
}
- (void)addNameLabelConstraints {
NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:self.nameLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.reasonLabel attribute:NSLayoutAttributeLeading multiplier:1 constant:0];
NSLayoutConstraint *right = [NSLayoutConstraint constraintWithItem:self.nameLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.reasonLabel attribute:NSLayoutAttributeTrailing multiplier:1 constant:0];
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:self.nameLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.reasonLabel attribute:NSLayoutAttributeBottom multiplier:1 constant:kLLGeneralMargin / 2.0];
self.nameLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.nameLabel.superview addConstraints:@[left, right, top]];
}
- (void)addDateLabelConstraints {
NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:self.dateLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.reasonLabel attribute:NSLayoutAttributeLeading multiplier:1 constant:0];
NSLayoutConstraint *right = [NSLayoutConstraint constraintWithItem:self.dateLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.reasonLabel attribute:NSLayoutAttributeTrailing multiplier:1 constant:0];
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:self.dateLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.nameLabel attribute:NSLayoutAttributeBottom multiplier:1 constant:kLLGeneralMargin / 2.0];
NSLayoutConstraint *bottom = [NSLayoutConstraint constraintWithItem:self.dateLabel attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.dateLabel.superview attribute:NSLayoutAttributeBottom multiplier:1 constant:-kLLGeneralMargin];
bottom.priority = UILayoutPriorityDefaultHigh;
self.dateLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.dateLabel.superview addConstraints:@[left, right, top, bottom]];
}
#pragma mark - Getters and setters
- (UILabel *)reasonLabel {
if (!_reasonLabel) {
_reasonLabel = [LLFactory getLabel];
_reasonLabel.font = [UIFont boldSystemFontOfSize:17];
_reasonLabel.numberOfLines = 0;
_reasonLabel.lineBreakMode = NSLineBreakByCharWrapping;
}
return _reasonLabel;
}
- (UILabel *)nameLabel {
if (!_nameLabel) {
_nameLabel = [LLFactory getLabel];
_nameLabel.font = [UIFont systemFontOfSize:14];
_nameLabel.numberOfLines = 0;
_nameLabel.lineBreakMode = NSLineBreakByCharWrapping;
}
return _nameLabel;
}
- (UILabel *)dateLabel {
if (!_dateLabel) {
_dateLabel = [LLFactory getLabel];
_dateLabel.font = [UIFont systemFontOfSize:12];
}
return _dateLabel;
}
@end

View File

@@ -0,0 +1,40 @@
//
// LLCrashDetailViewController.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLBaseTableViewController.h"
@class LLCrashModel;
NS_ASSUME_NONNULL_BEGIN
/// Crash detail view controller.
@interface LLCrashDetailViewController : LLBaseTableViewController
/**
* Model to show
*/
@property (nonatomic, strong) LLCrashModel *model;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,196 @@
//
// LLCrashDetailViewController.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLCrashDetailViewController.h"
#import "LLSubTitleTableViewCell.h"
#import "LLInternalMacros.h"
#import "LLStorageManager.h"
#import "LLToastUtils.h"
#import "LLCrashModel.h"
#import "LLConfig.h"
#import "LLRouter+Network.h"
#import "LLRouter+Log.h"
static NSString *const kCrashContentCellID = @"CrashContentCellID";
@interface LLCrashDetailViewController () <UITableViewDataSource, LLSubTitleTableViewCellDelegate>
@property (nonatomic, strong) NSMutableArray *titleArray;
@property (nonatomic, strong) NSMutableArray *contentArray;
@property (nonatomic, strong) NSArray *canCopyArray;
@end
@implementation LLCrashDetailViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title = self.model.name;
self.tableView.dataSource = self;
[self.tableView registerClass:[LLSubTitleTableViewCell class] forCellReuseIdentifier:kCrashContentCellID];
self.titleArray = [[NSMutableArray alloc] init];
self.contentArray = [[NSMutableArray alloc] init];
[self loadData];
}
#pragma mark - UITableViewDelegate, UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.titleArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
LLSubTitleTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCrashContentCellID];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.titleLabel.text = self.titleArray[indexPath.row];
cell.contentText = self.contentArray[indexPath.row];
cell.delegate = self;
NSString *title = self.titleArray[indexPath.row];
if ([title isEqualToString:@"Logs"] || [title isEqualToString:@"Network Requests"] || [title hasPrefix:@"Signal"]) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *title = self.titleArray[indexPath.row];
if ([title isEqualToString:@"Logs"]) {
UIViewController *vc = [LLRouter logViewControllerWithLaunchDate:self.model.launchDate];
if (vc) {
[self.navigationController pushViewController:vc animated:YES];
}
} else if ([title isEqualToString:@"Network Requests"]) {
UIViewController *vc = [LLRouter networkViewControllerWithLaunchDate:self.model.launchDate];
if (vc) {
[self.navigationController pushViewController:vc animated:YES];
}
} else if ([self.canCopyArray containsObject:title]) {
[[UIPasteboard generalPasteboard] setString:self.contentArray[indexPath.row]];
[[LLToastUtils shared] toastMessage:[NSString stringWithFormat:LLLocalizedString(@"copy.success"),title]];
}
}
- (void)LLSubTitleTableViewCell:(LLSubTitleTableViewCell *)cell didSelectedContentView:(UITextView *)contentTextView {
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];
}
#pragma mark - Primary
- (void)loadData {
__weak typeof(self) weakSelf = self;
[[LLToastUtils shared] loadingMessage:LLLocalizedString(@"loading")];
[[LLStorageManager shared] getModels:[LLRouter logModelClass] launchDate:_model.launchDate complete:^(NSArray<LLStorageModel *> *result) {
// Get log models.
__block NSArray *logs = result;
[[LLStorageManager shared] getModels:[LLRouter networkModelClass] launchDate:weakSelf.model.launchDate complete:^(NSArray<LLStorageModel *> *result) {
[[LLToastUtils shared] hide];
// Get nework requests.
NSArray *networkRequests = result;
[weakSelf updateDataWithLogs:logs networkRequests:networkRequests];
}];
}];
}
- (void)updateDataWithLogs:(NSArray *)logs networkRequests:(NSArray *)networkRequests {
[self.titleArray removeAllObjects];
[self.contentArray removeAllObjects];
if (_model.name) {
[self.titleArray addObject:@"Name"];
[self.contentArray addObject:_model.name];
}
if (_model.reason) {
[self.titleArray addObject:@"Reason"];
[self.contentArray addObject:_model.reason];
}
if (_model.date) {
[self.titleArray addObject:@"Date"];
[self.contentArray addObject:_model.date];
}
if (logs.count) {
[self.titleArray addObject:@"Logs"];
[self.contentArray addObject:[NSString stringWithFormat:@"%ld logs",(unsigned long)logs.count]];
}
if (networkRequests.count) {
[self.titleArray addObject:@"Network Requests"];
[self.contentArray addObject:[NSString stringWithFormat:@"%ld network requests",(unsigned long)networkRequests.count]];
}
if (_model.thread) {
[self.titleArray addObject:@"Thread"];
[self.contentArray addObject:_model.thread];
}
if (_model.userIdentity) {
[self.titleArray addObject:@"User Identity"];
[self.contentArray addObject:_model.userIdentity];
}
if (_model.stackSymbols.count) {
[self.titleArray addObject:@"Stack Symbols"];
NSMutableString *mutStr = [[NSMutableString alloc] init];
for (NSString *symbol in _model.stackSymbols) {
[mutStr appendFormat:@"%@\n\n",symbol];
}
[self.contentArray addObject:mutStr];
}
if (_model.userInfo.allKeys.count) {
[self.titleArray addObject:@"UserInfo"];
NSMutableString *content = [[NSMutableString alloc] init];
for (NSString *key in _model.userInfo.allKeys) {
[content appendFormat:@"%@ : %@\n",key,_model.userInfo[key]];
}
[self.contentArray addObject:content];
}
if (_model.appInfos.count) {
[self.titleArray addObject:@"App Infos"];
NSMutableString *str = [[NSMutableString alloc] init];
for (NSArray *array in _model.appInfos) {
for (NSDictionary *dic in array) {
for (NSString *key in dic) {
[str appendFormat:@"%@ : %@\n",key,dic[key]];
}
}
[str appendString:@"\n"];
}
[self.contentArray addObject:str];
}
[self.tableView reloadData];
}
- (NSArray *)canCopyArray {
if (!_canCopyArray) {
_canCopyArray = @[@"Name",@"Reason",@"Stack Symbols"];
}
return _canCopyArray;
}
@end

View File

@@ -0,0 +1,33 @@
//
// LLCrashViewController.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLEditTableViewController.h"
NS_ASSUME_NONNULL_BEGIN
/// Crash function view controller.
@interface LLCrashViewController : LLEditTableViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,137 @@
//
// LLCrashViewController.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLCrashViewController.h"
#import "LLCrashDetailViewController.h"
#import "LLImageNameConfig.h"
#import "LLInternalMacros.h"
#import "LLStorageManager.h"
#import "LLCrashHelper.h"
#import "LLToastUtils.h"
#import "LLCrashModel.h"
#import "LLCrashCell.h"
#import "LLConfig.h"
#import "UIViewController+LL_Utils.h"
static NSString *const kCrashCellID = @"CrashCellID";
@interface LLCrashViewController ()
@end
@implementation LLCrashViewController
- (instancetype)init
{
self = [super init];
if (self) {
self.isSearchEnable = YES;
self.isSelectEnable = YES;
self.isDeleteEnable = YES;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.title = LLLocalizedString(@"function.crash");
[self.tableView registerClass:[LLCrashCell class] forCellReuseIdentifier:kCrashCellID];
[self loadData];
}
#pragma mark - Primary
- (void)loadData {
__weak typeof(self) weakSelf = self;
[[LLToastUtils shared] loadingMessage:LLLocalizedString(@"loading")];
[[LLStorageManager shared] getModels:[LLCrashModel class] launchDate:nil complete:^(NSArray<LLStorageModel *> *result) {
[[LLToastUtils shared] hide];
[weakSelf.oriDataArray removeAllObjects];
[weakSelf.oriDataArray addObjectsFromArray:result];
[weakSelf.tableView reloadData];
}];
}
#pragma mark - Rewrite
- (void)deleteFilesWithIndexPaths:(NSArray *)indexPaths {
[super deleteFilesWithIndexPaths:indexPaths];
__block NSMutableArray *models = [[NSMutableArray alloc] init];
for (NSIndexPath *indexPath in indexPaths) {
[models addObject:self.datas[indexPath.row]];
}
__weak typeof(self) weakSelf = self;
[[LLToastUtils shared] loadingMessage:LLLocalizedString(@"deleting")];
[[LLStorageManager shared] removeModels:models complete:^(BOOL result) {
[[LLToastUtils shared] hide];
if (result) {
[weakSelf.oriDataArray removeObjectsInArray:models];
[weakSelf.searchDataArray removeObjectsInArray:models];
[weakSelf.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
} else {
[weakSelf LL_showAlertControllerWithMessage:LLLocalizedString(@"remove.fail") handler:^(NSInteger action) {
if (action == 1) {
[weakSelf loadData];
}
}];
}
}];
}
#pragma mark - UITableView
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
LLCrashCell *cell = [tableView dequeueReusableCellWithIdentifier:kCrashCellID forIndexPath:indexPath];
[cell confirmWithModel:self.datas[indexPath.row]];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
if (!self.tableView.isEditing) {
LLCrashDetailViewController *vc = [[LLCrashDetailViewController alloc] init];
vc.model = self.datas[indexPath.row];
[self.navigationController pushViewController:vc animated:YES];
}
}
#pragma mark - UITextFieldDelegate
- (void)textFieldDidChange:(NSString *)text {
[super textFieldDidChange:text];
if (text.length == 0) {
[self.searchDataArray removeAllObjects];
[self.searchDataArray addObjectsFromArray:self.oriDataArray];
[self.tableView reloadData];
} else {
[self.searchDataArray removeAllObjects];
for (LLCrashModel *model in self.oriDataArray) {
if ([model.name.lowercaseString containsString:text.lowercaseString] || [model.reason.lowercaseString containsString:text.lowercaseString]) {
[self.searchDataArray addObject:model];
}
}
[self.tableView reloadData];
}
}
@end

View File

@@ -0,0 +1,33 @@
//
// LLCrashWindow.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLComponentWindow.h"
NS_ASSUME_NONNULL_BEGIN
/// Crash window function.
@interface LLCrashWindow : LLComponentWindow
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,47 @@
//
// LLCrashWindow.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLCrashWindow.h"
#import "LLNavigationController.h"
#import "LLCrashViewController.h"
#import "LLWindowManager.h"
@implementation LLCrashWindow
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.showAnimateStyle = LLBaseWindowShowAnimateStylePresent;
self.hideAnimateStyle = LLBaseWindowHideAnimateStyleDismiss;
if (!self.rootViewController) {
self.rootViewController = [[LLNavigationController alloc] initWithRootViewController:[[LLCrashViewController alloc] init]];
}
}
return self;
}
- (void)componentDidFinish {
[[LLWindowManager shared] showEntryWindow];
}
@end

View File

@@ -0,0 +1,41 @@
//
// LLWindowManager+Crash.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLWindowManager.h"
#import "LLCrashWindow.h"
NS_ASSUME_NONNULL_BEGIN
@interface LLWindowManager (Crash)
/**
Crash window
@return LLCrashWindow.
*/
+ (LLCrashWindow *)crashWindow;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,32 @@
//
// LLWindowManager+Crash.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLWindowManager+Crash.h"
@implementation LLWindowManager (Crash)
+ (LLCrashWindow *)crashWindow {
return (LLCrashWindow *)[self createWindowWithClassName:NSStringFromClass([LLCrashWindow class])];
}
@end

View File

@@ -0,0 +1,151 @@
//
// LLEnumDescription.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/// Enum description helper.
@interface LLEnumDescription : NSObject
+ (NSString *_Nullable)lineBreakModeDescription:(NSLineBreakMode)mode;
+ (NSArray <NSString *>*)lineBreaks;
+ (NSString *_Nullable)userInterfaceStyleDescription:(UIUserInterfaceStyle)style API_AVAILABLE(ios(12.0));
+ (NSString *_Nullable)userInterfaceSizeClassDescription:(UIUserInterfaceSizeClass)sizeClass;
+ (NSString *_Nullable)traitEnvironmentLayoutDirectionDescription:(UITraitEnvironmentLayoutDirection)direction API_AVAILABLE(ios(10.0));
+ (NSString *_Nullable)viewContentModeDescription:(UIViewContentMode)mode;
+ (NSArray <NSString *>*)viewContentModeDescriptions;
+ (NSString *_Nullable)textAlignmentDescription:(NSTextAlignment)textAlignment;
+ (NSArray <NSString *>*)textAlignments;
+ (NSString *_Nullable)baselineAdjustmentDescription:(UIBaselineAdjustment)baselineAdjustment;
+ (NSArray <NSString *>*)baselineAdjustments;
+ (NSString *_Nullable)controlContentVerticalAlignmentDescription:(UIControlContentVerticalAlignment)contentVerticalAlignment;
+ (NSArray <NSString *>*)controlContentVerticalAlignments;
+ (NSString *_Nullable)controlContentHorizontalAlignmentDescription:(UIControlContentHorizontalAlignment)contentHorizontalAlignment;
+ (NSArray <NSString *>*)controlContentHorizontalAlignments;
+ (NSString *_Nullable)buttonTypeDescription:(UIButtonType)buttonType;
+ (NSString *_Nullable)controlStateDescription:(UIControlState)state;
+ (NSString *_Nullable)textBorderStyleDescription:(UITextBorderStyle)style;
+ (NSArray <NSString *>*)textBorderStyles;
+ (NSString *_Nullable)textFieldViewModeDescription:(UITextFieldViewMode)mode;
+ (NSArray <NSString *>*)textFieldViewModes;
+ (NSString *_Nullable)textAutocapitalizationTypeDescription:(UITextAutocapitalizationType)type;
+ (NSArray <NSString *>*)textAutocapitalizationTypes;
+ (NSString *_Nullable)textAutocorrectionTypeDescription:(UITextAutocorrectionType)type;
+ (NSArray <NSString *>*)textAutocorrectionTypes;
+ (NSString *_Nullable)keyboardTypeDescription:(UIKeyboardType)type;
+ (NSArray <NSString *>*)keyboardTypes;
+ (NSString *_Nullable)keyboardAppearanceDescription:(UIKeyboardAppearance)appearance;
+ (NSArray <NSString *>*)keyboardAppearances;
+ (NSString *_Nullable)returnKeyTypeDescription:(UIReturnKeyType)type;
+ (NSArray <NSString *>*)returnKeyTypes;
+ (NSString *_Nullable)activityIndicatorViewStyleDescription:(UIActivityIndicatorViewStyle)style;
+ (NSArray <NSString *>*)activityIndicatorViewStyles;
+ (NSString *_Nullable)progressViewStyleDescription:(UIProgressViewStyle)style;
+ (NSArray <NSString *>*)progressViewStyles;
+ (NSString *_Nullable)scrollViewIndicatorStyleDescription:(UIScrollViewIndicatorStyle)style;
+ (NSArray <NSString *>*)scrollViewIndicatorStyles;
+ (NSString *_Nullable)scrollViewKeyboardDismissModeDescription:(UIScrollViewKeyboardDismissMode)mode;
+ (NSArray <NSString *>*)scrollViewKeyboardDismissModes;
+ (NSString *_Nullable)tableViewStyleDescription:(UITableViewStyle)style;
+ (NSString *_Nullable)tableViewCellSeparatorStyleDescription:(UITableViewCellSeparatorStyle)style;
+ (NSArray <NSString *>*)tableViewCellSeparatorStyles;
+ (NSString *_Nullable)tableViewSeparatorInsetReferenceDescription:(UITableViewSeparatorInsetReference)reference API_AVAILABLE(ios(11.0));
+ (NSArray <NSString *>*)tableViewSeparatorInsetReferences API_AVAILABLE(ios(11.0));
+ (NSString *_Nullable)tableViewCellSelectionStyleDescription:(UITableViewCellSelectionStyle)style;
+ (NSArray <NSString *>*)tableViewCellSelectionStyles;
+ (NSString *_Nullable)tableViewCellAccessoryTypeDescription:(UITableViewCellAccessoryType)type;
+ (NSArray <NSString *>*)tableViewCellAccessoryTypes;
+ (NSString *_Nullable)datePickerModeDescription:(UIDatePickerMode)mode;
+ (NSArray <NSString *>*)datePickerModes;
+ (NSString *_Nullable)barStyleDescription:(UIBarStyle)style;
+ (NSArray <NSString *>*)barStyles;
+ (NSString *_Nullable)searchBarStyleDescription:(UISearchBarStyle)style;
+ (NSArray <NSString *>*)searchBarStyles;
+ (NSString *_Nullable)tabBarItemPositioningDescription:(UITabBarItemPositioning)positioning;
+ (NSArray <NSString *>*)tabBarItemPositionings;
+ (NSString *_Nullable)layoutAttributeDescription:(NSLayoutAttribute)attribute;
+ (NSArray <NSString *>*)layoutAttributes;
+ (NSString *_Nullable)layoutRelationDescription:(NSLayoutRelation)relation;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,896 @@
//
// LLEnumDescription.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLEnumDescription.h"
@implementation LLEnumDescription
+ (NSString *)lineBreakModeDescription:(NSLineBreakMode)mode {
switch (mode) {
case NSLineBreakByWordWrapping:
return @"Word Wrapping";
case NSLineBreakByCharWrapping:
return @"Char Wrapping";
case NSLineBreakByClipping:
return @"Clipping";
case NSLineBreakByTruncatingHead:
return @"Truncating Head";
case NSLineBreakByTruncatingMiddle:
return @"Truncating Middle";
case NSLineBreakByTruncatingTail:
return @"Truncation Tail";
}
return nil;
}
+ (NSArray <NSString *>*)lineBreaks {
NSMutableArray *breaks = [[NSMutableArray alloc] init];
for (NSLineBreakMode i = NSLineBreakByWordWrapping; i <= NSLineBreakByTruncatingMiddle; i++) {
NSString *lineBreak = [self lineBreakModeDescription:i];
if (lineBreak) {
[breaks addObject:lineBreak];
}
}
return [breaks copy];
}
+ (NSString *)userInterfaceStyleDescription:(UIUserInterfaceStyle)style {
switch (style) {
case UIUserInterfaceStyleUnspecified:
return @"Unspecified";
case UIUserInterfaceStyleLight:
return @"Light User Interface Style";
case UIUserInterfaceStyleDark:
return @"Dark User Interface Style";
}
return nil;
}
+ (NSString *)userInterfaceSizeClassDescription:(UIUserInterfaceSizeClass)sizeClass {
switch (sizeClass) {
case UIUserInterfaceSizeClassUnspecified:
return @"Unspecified";
case UIUserInterfaceSizeClassCompact:
return @"Compact Size Class";
case UIUserInterfaceSizeClassRegular:
return @"Regular Size Class";
}
return nil;
}
+ (NSString *)traitEnvironmentLayoutDirectionDescription:(UITraitEnvironmentLayoutDirection)direction {
switch (direction) {
case UITraitEnvironmentLayoutDirectionUnspecified:
return @"Unspecified";
case UITraitEnvironmentLayoutDirectionLeftToRight:
return @"Left To Right Layout";
case UITraitEnvironmentLayoutDirectionRightToLeft:
return @"Right To Left Layout";
}
return nil;
}
+ (NSString *)viewContentModeDescription:(UIViewContentMode)mode {
switch (mode) {
case UIViewContentModeScaleToFill:
return @"ScaleToFill";
case UIViewContentModeScaleAspectFit:
return @"ScaleAspectFit";
case UIViewContentModeScaleAspectFill:
return @"ScaleAspectFill";
case UIViewContentModeRedraw:
return @"Redraw";
case UIViewContentModeCenter:
return @"Center";
case UIViewContentModeTop:
return @"Top";
case UIViewContentModeBottom:
return @"Bottom";
case UIViewContentModeLeft:
return @"Left";
case UIViewContentModeRight:
return @"Right";
case UIViewContentModeTopLeft:
return @"TopLeft";
case UIViewContentModeTopRight:
return @"TopRight";
case UIViewContentModeBottomLeft:
return @"BottomLeft";
case UIViewContentModeBottomRight:
return @"BottomRight";
}
return nil;
}
+ (NSArray <NSString *>*)viewContentModeDescriptions {
NSMutableArray *descriptions = [[NSMutableArray alloc] init];
for (UIViewContentMode i = UIViewContentModeScaleToFill; i <= UIViewContentModeBottomRight; i++) {
NSString *description = [self viewContentModeDescription:i];
if (description) {
[descriptions addObject:description];
}
}
return [descriptions copy];
}
+ (NSString *)textAlignmentDescription:(NSTextAlignment)textAlignment {
switch (textAlignment) {
case NSTextAlignmentLeft:
return @"Left";
case NSTextAlignmentRight:
return @"Right";
case NSTextAlignmentCenter:
return @"Center";
case NSTextAlignmentJustified:
return @"Justified";
case NSTextAlignmentNatural:
return @"Natural";
}
return nil;
}
+ (NSArray <NSString *>*)textAlignments {
NSMutableArray *alignments = [[NSMutableArray alloc] init];
for (NSTextAlignment i = NSTextAlignmentLeft; i <= NSTextAlignmentNatural; i++) {
NSString *alignment = [self textAlignmentDescription:i];
if (alignment) {
[alignments addObject:alignment];
}
}
return [alignments copy];
}
+ (NSString *)baselineAdjustmentDescription:(UIBaselineAdjustment)baselineAdjustment {
switch (baselineAdjustment) {
case UIBaselineAdjustmentAlignBaselines:
return @"Baselines";
case UIBaselineAdjustmentAlignCenters:
return @"Centers";
case UIBaselineAdjustmentNone:
return @"None";
}
return nil;
}
+ (NSArray <NSString *>*)baselineAdjustments {
NSMutableArray *adjustments = [[NSMutableArray alloc] init];
for (UIBaselineAdjustment i = UIBaselineAdjustmentAlignBaselines; i <= UIBaselineAdjustmentNone; i++) {
NSString *adjustment = [self baselineAdjustmentDescription:i];
if (adjustment) {
[adjustments addObject:adjustment];
}
}
return adjustments;
}
+ (NSString *)controlContentVerticalAlignmentDescription:(UIControlContentVerticalAlignment)contentVerticalAlignment {
switch (contentVerticalAlignment) {
case UIControlContentVerticalAlignmentCenter:
return @"Centered";
case UIControlContentVerticalAlignmentTop:
return @"Top";
case UIControlContentVerticalAlignmentBottom:
return @"Bottom";
case UIControlContentVerticalAlignmentFill:
return @"Fill";
}
return nil;
}
+ (NSArray <NSString *>*)controlContentVerticalAlignments {
NSMutableArray *alignments = [[NSMutableArray alloc] init];
for (UIControlContentVerticalAlignment i = UIControlContentVerticalAlignmentCenter; i <= UIControlContentVerticalAlignmentFill; i++) {
NSString *alignment = [self controlContentVerticalAlignmentDescription:i];
if (alignment) {
[alignments addObject:alignment];
}
}
return [alignments copy];
}
+ (NSString *)controlContentHorizontalAlignmentDescription:(UIControlContentHorizontalAlignment)contentHorizontalAlignment {
switch (contentHorizontalAlignment) {
case UIControlContentHorizontalAlignmentCenter:
return @"Centered";
case UIControlContentHorizontalAlignmentLeft:
return @"Left";
case UIControlContentHorizontalAlignmentRight:
return @"Right";
case UIControlContentHorizontalAlignmentFill:
return @"Fill";
case UIControlContentHorizontalAlignmentLeading:
return @"Leading";
case UIControlContentHorizontalAlignmentTrailing:
return @"Trailing";
}
return nil;
}
+ (NSArray <NSString *>*)controlContentHorizontalAlignments {
NSMutableArray *alignments = [[NSMutableArray alloc] init];
UIControlContentHorizontalAlignment max = UIControlContentHorizontalAlignmentFill;
if (@available(iOS 11.0, *)) {
max = UIControlContentHorizontalAlignmentTrailing;
}
for (UIControlContentHorizontalAlignment i = UIControlContentHorizontalAlignmentCenter; i <= max; i++) {
NSString *alignment = [self controlContentHorizontalAlignmentDescription:i];
if (alignment) {
[alignments addObject:alignment];
}
}
return [alignments copy];
}
+ (NSString *)buttonTypeDescription:(UIButtonType)buttonType {
switch (buttonType) {
case UIButtonTypeCustom:
return @"Custom";
case UIButtonTypeSystem:
return @"System";
case UIButtonTypeDetailDisclosure:
return @"Detail Disclosure";
case UIButtonTypeInfoLight:
return @"Info Light";
case UIButtonTypeInfoDark:
return @"Info Dark";
case UIButtonTypeContactAdd:
return @"Contact Add";
#ifdef __IPHONE_13_0
case UIButtonTypeClose:
return @"Close";
#endif
default:
break;
}
return nil;
}
+ (NSString *)controlStateDescription:(UIControlState)state {
switch (state) {
case UIControlStateNormal:
return @"Normal";
case UIControlStateFocused:
return @"Focused";
case UIControlStateDisabled:
return @"Disabled";
case UIControlStateReserved:
return @"Reserved";
case UIControlStateSelected:
return @"Selected";
case UIControlStateApplication:
return @"Application";
case UIControlStateHighlighted:
return @"Highlighted";
}
return nil;
}
+ (NSString *)textBorderStyleDescription:(UITextBorderStyle)style {
switch (style) {
case UITextBorderStyleNone:
return @"None";
case UITextBorderStyleLine:
return @"Line";
case UITextBorderStyleBezel:
return @"Bezel";
case UITextBorderStyleRoundedRect:
return @"Rounded Rect";
}
return nil;
}
+ (NSArray <NSString *>*)textBorderStyles {
NSMutableArray *styles = [[NSMutableArray alloc] init];
for (UITextBorderStyle i = UITextBorderStyleNone; i <= UITextBorderStyleRoundedRect; i++) {
NSString *style = [self textBorderStyleDescription:i];
if (style) {
[styles addObject:style];
}
}
return [styles copy];
}
+ (NSString *)textFieldViewModeDescription:(UITextFieldViewMode)mode {
switch (mode) {
case UITextFieldViewModeNever:
return @"Never appears";
case UITextFieldViewModeWhileEditing:
return @"While editing";
case UITextFieldViewModeUnlessEditing:
return @"Unless editing";
case UITextFieldViewModeAlways:
return @"Always appears";
}
return nil;
}
+ (NSArray <NSString *>*)textFieldViewModes {
NSMutableArray *modes = [[NSMutableArray alloc] init];
for (UITextFieldViewMode i = UITextFieldViewModeNever; i <= UITextFieldViewModeAlways; i++) {
NSString *mode = [self textFieldViewModeDescription:i];
if (mode) {
[modes addObject:mode];
}
}
return [modes copy];
}
+ (NSString *)textAutocapitalizationTypeDescription:(UITextAutocapitalizationType)type {
switch (type) {
case UITextAutocapitalizationTypeNone:
return @"None";
case UITextAutocapitalizationTypeWords:
return @"Words";
case UITextAutocapitalizationTypeSentences:
return @"Sentences";
case UITextAutocapitalizationTypeAllCharacters:
return @"All Characters";
}
return nil;
}
+ (NSArray <NSString *>*)textAutocapitalizationTypes {
NSMutableArray *types = [[NSMutableArray alloc] init];
for (UITextAutocapitalizationType i = UITextAutocapitalizationTypeNone; i <= UITextAutocapitalizationTypeAllCharacters; i++) {
NSString *type = [self textAutocapitalizationTypeDescription:i];
if (type) {
[types addObject:type];
}
}
return [types copy];
}
+ (NSString *)textAutocorrectionTypeDescription:(UITextAutocorrectionType)type {
switch (type) {
case UITextAutocorrectionTypeDefault:
return @"Default";
case UITextAutocorrectionTypeNo:
return @"NO";
case UITextAutocorrectionTypeYes:
return @"YES";
}
return nil;
}
+ (NSArray <NSString *>*)textAutocorrectionTypes {
NSMutableArray *types = [[NSMutableArray alloc] init];
for (UITextAutocorrectionType i = 0; i <= UITextAutocorrectionTypeYes; i++) {
NSString *type = [self textAutocorrectionTypeDescription:i];
if (type) {
[types addObject:type];
}
}
return [types copy];
}
+ (NSString *)keyboardTypeDescription:(UIKeyboardType)type {
switch (type) {
case UIKeyboardTypeDefault:
return @"Default";
case UIKeyboardTypeASCIICapable:
return @"ASCII capable";
case UIKeyboardTypeNumbersAndPunctuation:
return @"Numbers and punctuation";
case UIKeyboardTypeURL:
return @"URL";
case UIKeyboardTypeNumberPad:
return @"Number pad";
case UIKeyboardTypePhonePad:
return @"Phone pad";
case UIKeyboardTypeNamePhonePad:
return @"Name phone pad";
case UIKeyboardTypeEmailAddress:
return @"Email address";
case UIKeyboardTypeDecimalPad:
return @"Decimal pad";
case UIKeyboardTypeTwitter:
return @"Twitter";
case UIKeyboardTypeWebSearch:
return @"Web search";
case UIKeyboardTypeASCIICapableNumberPad:
return @"ASCII capable number pad";
}
return nil;
}
+ (NSArray <NSString *>*)keyboardTypes {
NSMutableArray *types = [[NSMutableArray alloc] init];
UIKeyboardType max = UIKeyboardTypeWebSearch;
if (@available(iOS 10.0, *)) {
max = UIKeyboardTypeASCIICapableNumberPad;
}
for (UIKeyboardType i = UIKeyboardTypeDefault; i <= max; i++) {
NSString *type = [self keyboardTypeDescription:i];
if (type) {
[types addObject:type];
}
}
return [types copy];
}
+ (NSString *)keyboardAppearanceDescription:(UIKeyboardAppearance)appearance {
switch (appearance) {
case UIKeyboardAppearanceDefault:
return @"Default";
case UIKeyboardAppearanceDark:
return @"Dark";
case UIKeyboardAppearanceLight:
return @"Light";
}
return nil;
}
+ (NSArray <NSString *>*)keyboardAppearances {
NSMutableArray *appearances = [[NSMutableArray alloc] init];
for (UIKeyboardAppearance i = UIKeyboardAppearanceDefault; i <= UIKeyboardAppearanceLight; i++) {
NSString *appearance = [self keyboardAppearanceDescription:i];
if (appearance) {
[appearances addObject:appearance];
}
}
return [appearances copy];
}
+ (NSString *)returnKeyTypeDescription:(UIReturnKeyType)type {
switch (type) {
case UIReturnKeyDefault:
return @"Default";
case UIReturnKeyGo:
return @"Go";
case UIReturnKeyGoogle:
return @"Google";
case UIReturnKeyJoin:
return @"Join";
case UIReturnKeyNext:
return @"Next";
case UIReturnKeyRoute:
return @"Route";
case UIReturnKeySearch:
return @"Search";
case UIReturnKeySend:
return @"Send";
case UIReturnKeyYahoo:
return @"Yahoo";
case UIReturnKeyDone:
return @"Done";
case UIReturnKeyEmergencyCall:
return @"Emergency call";
case UIReturnKeyContinue:
return @"Continue";
}
return nil;
}
+ (NSArray <NSString *>*)returnKeyTypes {
NSMutableArray *types = [[NSMutableArray alloc] init];
UIReturnKeyType max = UIReturnKeyEmergencyCall;
if (@available(iOS 9.0, *)) {
max = UIReturnKeyContinue;
}
for (UIReturnKeyType i = UIReturnKeyDefault; i <= max; i++) {
NSString *type = [self returnKeyTypeDescription:i];
if (type) {
[types addObject:type];
}
}
return [types copy];
}
+ (NSString *)activityIndicatorViewStyleDescription:(UIActivityIndicatorViewStyle)style {
switch (style) {
#ifdef __IPHONE_13_0
case UIActivityIndicatorViewStyleMedium:
return @"Medium";
case UIActivityIndicatorViewStyleLarge:
return @"Large";
#endif
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
case UIActivityIndicatorViewStyleWhiteLarge:
return @"White Large";
case UIActivityIndicatorViewStyleWhite:
return @"White";
case UIActivityIndicatorViewStyleGray:
return @"Gray";
#pragma clang diagnostic pop
}
return nil;
}
+ (NSArray <NSString *>*)activityIndicatorViewStyles {
NSMutableArray *styles = [[NSMutableArray alloc] init];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
NSArray *actions = @[@(UIActivityIndicatorViewStyleWhiteLarge),@(UIActivityIndicatorViewStyleWhite),@(UIActivityIndicatorViewStyleGray)];
#ifdef __IPHONE_13_0
if (@available(iOS 13.0, *)) {
actions = @[@(UIActivityIndicatorViewStyleMedium),@(UIActivityIndicatorViewStyleLarge),@(UIActivityIndicatorViewStyleWhiteLarge),@(UIActivityIndicatorViewStyleWhite),@(UIActivityIndicatorViewStyleGray)];
}
#endif
#pragma clang diagnostic pop
for (NSNumber *num in actions) {
NSString *style = [self activityIndicatorViewStyleDescription:[num integerValue]];
if (style) {
[styles addObject:style];
}
}
return [styles copy];
}
+ (NSString *)progressViewStyleDescription:(UIProgressViewStyle)style {
switch (style) {
case UIProgressViewStyleDefault:
return @"Default";
case UIProgressViewStyleBar:
return @"Bar";
}
return nil;
}
+ (NSArray <NSString *>*)progressViewStyles {
NSMutableArray *styles = [[NSMutableArray alloc] init];
for (UIProgressViewStyle i = UIProgressViewStyleDefault; i <= UIProgressViewStyleBar; i++) {
NSString *style = [self progressViewStyleDescription:i];
if (style) {
[styles addObject:style];
}
}
return [styles copy];
}
+ (NSString *)scrollViewIndicatorStyleDescription:(UIScrollViewIndicatorStyle)style {
switch (style) {
case UIScrollViewIndicatorStyleDefault:
return @"Default";
case UIScrollViewIndicatorStyleBlack:
return @"Black";
case UIScrollViewIndicatorStyleWhite:
return @"White";
}
return nil;
}
+ (NSArray <NSString *>*)scrollViewIndicatorStyles {
NSMutableArray *styles = [[NSMutableArray alloc] init];
for (UIScrollViewIndicatorStyle i = UIScrollViewIndicatorStyleDefault; i <= UIScrollViewIndicatorStyleWhite; i++) {
NSString *style = [self scrollViewIndicatorStyleDescription:i];
if (style) {
[styles addObject:style];
}
}
return [styles copy];
}
+ (NSString *)scrollViewKeyboardDismissModeDescription:(UIScrollViewKeyboardDismissMode)mode {
switch (mode) {
case UIScrollViewKeyboardDismissModeNone:
return @"Do not dismiss";
case UIScrollViewKeyboardDismissModeOnDrag:
return @"Dismiss on drag";
case UIScrollViewKeyboardDismissModeInteractive:
return @"Dismiss interactively";
}
return nil;
}
+ (NSArray <NSString *>*)scrollViewKeyboardDismissModes {
NSMutableArray *modes = [[NSMutableArray alloc] init];
for (UIScrollViewKeyboardDismissMode i = UIScrollViewKeyboardDismissModeNone; i <= UIScrollViewKeyboardDismissModeInteractive; i++) {
NSString *mode = [self scrollViewKeyboardDismissModeDescription:i];
if (mode) {
[modes addObject:mode];
}
}
return [modes copy];
}
+ (NSString *)tableViewStyleDescription:(UITableViewStyle)style {
switch (style) {
case UITableViewStylePlain:
return @"Plain";
case UITableViewStyleGrouped:
return @"Grouped";
#ifdef __IPHONE_13_0
case UITableViewStyleInsetGrouped:
return @"Inset Grouped";
#endif
}
return nil;
}
+ (NSString *)tableViewCellSeparatorStyleDescription:(UITableViewCellSeparatorStyle)style {
switch (style) {
case UITableViewCellSeparatorStyleNone:
return @"None";
case UITableViewCellSeparatorStyleSingleLine:
return @"Single Line";
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
case UITableViewCellSeparatorStyleSingleLineEtched:
return @"Single Line Etched";
#pragma clang diagnostic pop
}
return nil;
}
+ (NSArray <NSString *>*)tableViewCellSeparatorStyles {
NSMutableArray *styles = [[NSMutableArray alloc] init];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
for (UITableViewCellSeparatorStyle i = UITableViewCellSeparatorStyleNone; i <= UITableViewCellSeparatorStyleSingleLineEtched; i++) {
NSString *style = [self tableViewCellSeparatorStyleDescription:i];
if (style) {
[styles addObject:style];
}
}
#pragma clang diagnostic pop
return [styles copy];
}
+ (NSString *)tableViewSeparatorInsetReferenceDescription:(UITableViewSeparatorInsetReference)reference {
switch (reference) {
case UITableViewSeparatorInsetFromCellEdges:
return @"From Cell Edges";
case UITableViewSeparatorInsetFromAutomaticInsets:
return @"Automatic Insets";
}
return nil;
}
+ (NSArray <NSString *>*)tableViewSeparatorInsetReferences {
NSMutableArray *references = [[NSMutableArray alloc] init];
for (UITableViewSeparatorInsetReference i = UITableViewSeparatorInsetFromCellEdges; i <= UITableViewSeparatorInsetFromAutomaticInsets; i++) {
NSString *reference = [self tableViewSeparatorInsetReferenceDescription:i];
if (reference) {
[references addObject:reference];
}
}
return [references copy];
}
+ (NSString *)tableViewCellSelectionStyleDescription:(UITableViewCellSelectionStyle)style {
switch (style) {
case UITableViewCellSelectionStyleNone:
return @"None";
case UITableViewCellSelectionStyleBlue:
return @"Blue";
case UITableViewCellSelectionStyleGray:
return @"Gray";
case UITableViewCellSelectionStyleDefault:
return @"Default";
}
return nil;
}
+ (NSArray <NSString *>*)tableViewCellSelectionStyles {
NSMutableArray *styles = [[NSMutableArray alloc] init];
for (UITableViewCellSelectionStyle i = UITableViewCellSelectionStyleNone; i <= UITableViewCellSelectionStyleDefault; i++) {
NSString *style = [self tableViewCellSelectionStyleDescription:i];
if (style) {
[styles addObject:style];
}
}
return [styles copy];
}
+ (NSString *)tableViewCellAccessoryTypeDescription:(UITableViewCellAccessoryType)type {
switch (type) {
case UITableViewCellAccessoryNone:
return @"None";
case UITableViewCellAccessoryDisclosureIndicator:
return @"Disclosure Indicator";
case UITableViewCellAccessoryDetailDisclosureButton:
return @"Disclosure Button";
case UITableViewCellAccessoryCheckmark:
return @"Checkmark";
case UITableViewCellAccessoryDetailButton:
return @"Detail Button";
}
return nil;
}
+ (NSArray <NSString *>*)tableViewCellAccessoryTypes {
NSMutableArray *types = [[NSMutableArray alloc] init];
for (UITableViewCellAccessoryType i = UITableViewCellAccessoryNone; i <= UITableViewCellAccessoryDetailButton; i++) {
NSString *type = [self tableViewCellAccessoryTypeDescription:i];
if (type) {
[types addObject:type];
}
}
return [types copy];
}
+ (NSString *)datePickerModeDescription:(UIDatePickerMode)mode {
switch (mode) {
case UIDatePickerModeDate:
return @"Date";
case UIDatePickerModeTime:
return @"Time";
case UIDatePickerModeDateAndTime:
return @"Date and Time";
case UIDatePickerModeCountDownTimer:
return @"Count Down Timer";
}
return nil;
}
+ (NSArray <NSString *>*)datePickerModes {
NSMutableArray *modes = [[NSMutableArray alloc] init];
for (UIDatePickerMode i = UIDatePickerModeDate; i <= UIDatePickerModeCountDownTimer; i++) {
NSString *mode = [self datePickerModeDescription:i];
if (mode) {
[modes addObject:mode];
}
}
return [modes copy];
}
+ (NSString *)barStyleDescription:(UIBarStyle)style {
switch (style) {
case UIBarStyleDefault:
return @"Default";
case UIBarStyleBlack:
return @"Black";
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
case UIBarStyleBlackTranslucent:
return @"Black Translucent";
#pragma clang diagnostic pop
}
return nil;
}
+ (NSArray <NSString *>*)barStyles {
NSMutableArray *styles = [[NSMutableArray alloc] init];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
for (UIBarStyle i = UIBarStyleDefault; i <= UIBarStyleBlackTranslucent; i++) {
NSString *style = [self barStyleDescription:i];
if (style) {
[styles addObject:style];
}
}
#pragma clang diagnostic pop
return [styles copy];
}
+ (NSString *)searchBarStyleDescription:(UISearchBarStyle)style {
switch (style) {
case UISearchBarStyleDefault:
return @"Default";
case UISearchBarStyleProminent:
return @"Prominent";
case UISearchBarStyleMinimal:
return @"Minimal";
}
return nil;
}
+ (NSArray <NSString *>*)searchBarStyles {
NSMutableArray *styles = [[NSMutableArray alloc] init];
for (UISearchBarStyle i = UISearchBarStyleDefault; i <= UISearchBarStyleMinimal; i++) {
NSString *style = [self searchBarStyleDescription:i];
if (style) {
[styles addObject:style];
}
}
return [styles copy];
}
+ (NSString *)tabBarItemPositioningDescription:(UITabBarItemPositioning)positioning {
switch (positioning) {
case UITabBarItemPositioningAutomatic:
return @"Automatic";
case UITabBarItemPositioningFill:
return @"Fill";
case UITabBarItemPositioningCentered:
return @"Centered";
}
return nil;
}
+ (NSArray <NSString *>*)tabBarItemPositionings {
NSMutableArray *positionings = [[NSMutableArray alloc] init];
for (UITabBarItemPositioning i = UITabBarItemPositioningAutomatic; i <= UITabBarItemPositioningCentered; i++) {
NSString *positioning = [self tabBarItemPositioningDescription:i];
if (positioning) {
[positionings addObject:positioning];
}
}
return [positionings copy];
}
+ (NSString *_Nullable)layoutAttributeDescription:(NSLayoutAttribute)attribute {
switch (attribute) {
case NSLayoutAttributeLeft:
return @"left";
case NSLayoutAttributeRight:
return @"right";
case NSLayoutAttributeTop:
return @"top";
case NSLayoutAttributeBottom:
return @"bottom";
case NSLayoutAttributeLeading:
return @"leading";
case NSLayoutAttributeTrailing:
return @"trailing";
case NSLayoutAttributeWidth:
return @"width";
case NSLayoutAttributeHeight:
return @"height";
case NSLayoutAttributeCenterX:
return @"centerX";
case NSLayoutAttributeCenterY:
return @"centerY";
case NSLayoutAttributeLastBaseline:
return @"lastBaseline";
case NSLayoutAttributeFirstBaseline:
return @"firstBaseline";
case NSLayoutAttributeLeftMargin:
return @"leftMargin";
case NSLayoutAttributeRightMargin:
return @"rightMargin";
case NSLayoutAttributeTopMargin:
return @"topMargin";
case NSLayoutAttributeBottomMargin:
return @"bottomMargin";
case NSLayoutAttributeLeadingMargin:
return @"leadingMargin";
case NSLayoutAttributeTrailingMargin:
return @"trailingMargin";
case NSLayoutAttributeCenterXWithinMargins:
return @"centerXMargin";
case NSLayoutAttributeCenterYWithinMargins:
return @"centerYMargin";
case NSLayoutAttributeNotAnAttribute:
return nil;
default:
return nil;
}
}
+ (NSArray <NSString *>*)layoutAttributes {
NSMutableArray *attributes = [[NSMutableArray alloc] init];
for (NSLayoutAttribute i = NSLayoutAttributeNotAnAttribute; i <= NSLayoutAttributeCenterYWithinMargins; i++) {
NSString *attribute = [self layoutAttributeDescription:i];
if (attribute) {
[attributes addObject:attribute];
}
}
return [attributes copy];
}
+ (NSString *_Nullable)layoutRelationDescription:(NSLayoutRelation)relation {
switch (relation) {
case NSLayoutRelationLessThanOrEqual:
return @"<=";
case NSLayoutRelationEqual:
return @"=";
case NSLayoutRelationGreaterThanOrEqual:
return @">=";
default:
return nil;
}
}
@end

View File

@@ -0,0 +1,45 @@
//
// LLHierarchyHelper.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <Foundation/Foundation.h>
@class UIWindow;
NS_ASSUME_NONNULL_BEGIN
/// Hierarchy function helper.
@interface LLHierarchyHelper : NSObject
/// Shared instance.
+ (instancetype _Nonnull)shared;
/// All window in application.
- (NSArray <UIWindow *>*)allWindows;
/// All window in application without class.
/// @param cls Ignore class.
- (NSArray <UIWindow *>*)allWindowsIgnoreClass:(Class _Nullable)cls;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,80 @@
//
// LLHierarchyHelper.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLHierarchyHelper.h"
#import <UIKit/UIKit.h>
static LLHierarchyHelper *_instance = nil;
@implementation LLHierarchyHelper
+ (instancetype)shared {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[LLHierarchyHelper alloc] init];
});
return _instance;
}
- (NSArray <UIWindow *>*)allWindows {
return [self allWindowsIgnoreClass:nil];
}
- (NSArray <UIWindow *>*)allWindowsIgnoreClass:(Class)cls {
BOOL includeInternalWindows = YES;
BOOL onlyVisibleWindows = NO;
SEL allWindowsSelector = NSSelectorFromString(@"allWindowsIncludingInternalWindows:onlyVisibleWindows:");
NSMethodSignature *methodSignature = [[UIWindow class] methodSignatureForSelector:allWindowsSelector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
invocation.target = [UIWindow class];
invocation.selector = allWindowsSelector;
[invocation setArgument:&includeInternalWindows atIndex:2];
[invocation setArgument:&onlyVisibleWindows atIndex:3];
[invocation invoke];
__unsafe_unretained NSArray<UIWindow *> *windows = nil;
[invocation getReturnValue:&windows];
windows = [windows sortedArrayUsingComparator:^NSComparisonResult(UIWindow * obj1, UIWindow * obj2) {
return obj1.windowLevel > obj2.windowLevel;
}];
NSMutableArray *results = [[NSMutableArray alloc] initWithArray:windows];
NSMutableArray *removeResults = [[NSMutableArray alloc] init];
if (cls != nil) {
for (UIWindow *window in results) {
if ([window isKindOfClass:cls]) {
[removeResults addObject:window];
}
}
}
[results removeObjectsInArray:removeResults];
return [NSArray arrayWithArray:results];
}
@end

View File

@@ -0,0 +1,52 @@
//
// NSObject+LL_Hierarchy.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <UIKit/UIKit.h>
@class LLTitleCellCategoryModel;
NS_ASSUME_NONNULL_BEGIN
FOUNDATION_EXPORT NSNotificationName const LLHierarchyChangeNotificationName;
@interface NSObject (LL_Hierarchy)
- (NSArray <LLTitleCellCategoryModel *>*)LL_hierarchyCategoryModels;
- (void)LL_showIntAlertAndAutomicSetWithKeyPath:(NSString *)keyPath;
- (void)LL_showFrameAlertAndAutomicSetWithKeyPath:(NSString *)keyPath;
- (void)LL_showColorAlertAndAutomicSetWithKeyPath:(NSString *)keyPath;
- (void)LL_showFontAlertAndAutomicSetWithKeyPath:(NSString *)keyPath;
@end
@interface UIView (LL_Hierarchy)
- (NSArray <LLTitleCellCategoryModel *>*)LL_sizeHierarchyCategoryModels;
@end
NS_ASSUME_NONNULL_END

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,42 @@
//
// LLHierarchy.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef LLHierarchy_h
#define LLHierarchy_h
#import "LLHierarchyComponent.h"
#import "LLHierarchyHelper.h"
#import "LLEnumDescription.h"
#import "NSObject+LL_Hierarchy.h"
#import "LLHierarchyDetailViewController.h"
#import "LLHierarchyViewController.h"
#import "LLHierarchyPickerView.h"
#import "LLHierarchyInfoView.h"
#import "LLHierarchyWindow.h"
#import "LLWindowManager+Hierarchy.h"
#endif /* LLHierarchy_h */

View File

@@ -0,0 +1,33 @@
//
// LLHierarchyComponent.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLComponent.h"
NS_ASSUME_NONNULL_BEGIN
/// Hierarchy function component.
@interface LLHierarchyComponent : LLComponent
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,34 @@
//
// LLHierarchyComponent.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLHierarchyComponent.h"
#import "LLWindowManager+Hierarchy.h"
@implementation LLHierarchyComponent
- (void)componentDidLoad:(NSDictionary *)data {
[[LLWindowManager shared] showWindow:[LLWindowManager hierarchyWindow] animated:YES];
}
@end

View File

@@ -0,0 +1,36 @@
//
// LLHierarchyDetailViewController.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLTitleViewController.h"
NS_ASSUME_NONNULL_BEGIN
/// Hierarchy detail view controller.
@interface LLHierarchyDetailViewController : LLTitleViewController
/// Selected view to detail.
@property (nonatomic, strong, nullable) UIView *selectView;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,145 @@
//
// LLHierarchyDetailViewController.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLHierarchyDetailViewController.h"
#import "LLTitleCellCategoryModel.h"
#import "LLDetailTitleCell.h"
#import "LLInternalMacros.h"
#import "LLFormatterTool.h"
#import "LLThemeManager.h"
#import "LLFactory.h"
#import "LLConfig.h"
#import "LLConst.h"
#import "UIViewController+LL_Utils.h"
#import "NSObject+LL_Hierarchy.h"
#import "UIButton+LL_Utils.h"
#import "UIImage+LL_Utils.h"
#import "UIView+LL_Utils.h"
@interface LLHierarchyDetailViewController ()
@property (nonatomic, strong) UISegmentedControl *segmentedControl;
@property (nonatomic, strong) NSMutableArray *objectDatas;
@property (nonatomic, strong) NSMutableArray *sizeDatas;
@end
@implementation LLHierarchyDetailViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSAssert(self.selectView, @"SelectView can't be nil");
self.title = LLLocalizedString(@"function.hierarchy.detail");
self.objectDatas = [[NSMutableArray alloc] init];
self.sizeDatas = [[NSMutableArray alloc] init];
UIView *headerView = ({
UIView *view = [LLFactory getView];
view.frame = CGRectMake(0, 0, LL_SCREEN_WIDTH, 30 + kLLGeneralMargin * 2);
view;
});
[headerView addSubview:self.segmentedControl];
self.tableView.tableHeaderView = headerView;
[self loadData];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveLLHierarchyChangeNotification:) name:LLHierarchyChangeNotificationName object:nil];
}
#pragma mark - Over write
- (void)leftItemClick:(UIButton *)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - UITableViewDataSource
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
if ([cell isKindOfClass:[LLDetailTitleCell class]]) {
LLDetailTitleCell *detailCell = (LLDetailTitleCell *)cell;
detailCell.detailLabel.textAlignment = NSTextAlignmentLeft;
}
LLTitleCellModel *model = self.dataArray[indexPath.section].items[indexPath.row];
cell.separatorInset = model.separatorInsets;
return cell;
}
#pragma mark - NSNotifications
- (void)didReceiveLLHierarchyChangeNotification:(NSNotification *)notification {
[self loadData];
}
#pragma mark - Event responses
- (void)segmentedControlValueChanged:(UISegmentedControl *)sender {
[self reloadTableView];
}
#pragma mark - Primary
- (void)loadData {
[self.objectDatas removeAllObjects];
NSArray *models = [self.selectView LL_hierarchyCategoryModels];
[self.objectDatas addObjectsFromArray:models];
[self.sizeDatas removeAllObjects];
NSArray *sizeModels = [self.selectView LL_sizeHierarchyCategoryModels];
[self.sizeDatas addObjectsFromArray:sizeModels];
[self reloadTableView];
}
- (void)reloadTableView {
[self.dataArray removeAllObjects];
if (self.segmentedControl.selectedSegmentIndex == 0) {
[self.dataArray addObjectsFromArray:self.objectDatas];
} else if (self.segmentedControl.selectedSegmentIndex == 1) {
[self.dataArray addObjectsFromArray:self.sizeDatas];
}
[self.tableView reloadData];
}
#pragma mark - Getters and setters
- (UISegmentedControl *)segmentedControl {
if (!_segmentedControl) {
_segmentedControl = [LLFactory getSegmentedControl:nil frame:CGRectMake(kLLGeneralMargin, kLLGeneralMargin, self.view.LL_width - kLLGeneralMargin * 2, 30) items:@[@"Object", @"Size"]];
[_segmentedControl setTitleTextAttributes:@{NSForegroundColorAttributeName : [LLThemeManager shared].primaryColor} forState:UIControlStateNormal];
[_segmentedControl setTitleTextAttributes:@{NSForegroundColorAttributeName : [LLThemeManager shared].backgroundColor} forState:UIControlStateSelected];
[_segmentedControl addTarget:self action:@selector(segmentedControlValueChanged:) forControlEvents:UIControlEventValueChanged];
_segmentedControl.backgroundColor = [LLThemeManager shared].containerColor;
_segmentedControl.tintColor = [LLThemeManager shared].primaryColor;
#ifdef __IPHONE_13_0
if (@available(iOS 13.0, *)) {
_segmentedControl.selectedSegmentTintColor = [LLThemeManager shared].primaryColor;
}
#endif
_segmentedControl.selectedSegmentIndex = 0;
}
return _segmentedControl;
}
@end

View File

@@ -0,0 +1,59 @@
//
// LLHierarchyInfoView.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLInfoView.h"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSUInteger, LLHierarchyInfoViewAction) {
LLHierarchyInfoViewActionShowParent,
LLHierarchyInfoViewActionShowSubview,
LLHierarchyInfoViewActionShowMoreInfo
};
@class LLHierarchyInfoView;
/// Hierarchy info view delegate.
@protocol LLHierarchyInfoViewDelegate <NSObject, LLInfoViewDelegate>
- (void)LLHierarchyInfoView:(LLHierarchyInfoView *)view didSelectAt:(LLHierarchyInfoViewAction)action;
@end
/// Hierarchy info view.
@interface LLHierarchyInfoView : LLInfoView
/// Action delegate.
@property (nonatomic, weak, nullable) id<LLHierarchyInfoViewDelegate> delegate;
/// Selected view, retain.
@property (nonatomic, strong, nullable, readonly) UIView *selectedView;
/// Update selected view.
/// @param view Selected view.
- (void)updateSelectedView:(UIView *)view;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,353 @@
//
// LLHierarchyInfoView.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLHierarchyInfoView.h"
#import "LLImageNameConfig.h"
#import "LLInternalMacros.h"
#import "LLThemeManager.h"
#import "LLToastUtils.h"
#import "LLFactory.h"
#import "LLConfig.h"
#import "LLConst.h"
#import "LLTool.h"
#import "NSObject+LL_Hierarchy.h"
#import "UIColor+LL_Utils.h"
#import "UIView+LL_Utils.h"
@interface LLHierarchyInfoView ()
@property (nonatomic, strong, nullable) UIView *selectedView;
@property (nonatomic, strong) UILabel *contentLabel;
@property (nonatomic, strong) UILabel *frameLabel;
@property (nonatomic, strong) UILabel *backgroundColorLabel;
@property (nonatomic, strong) UILabel *textColorLabel;
@property (nonatomic, strong) UILabel *fontLabel;
@property (nonatomic, strong) UILabel *tagLabel;
@property (nonatomic, strong) UIView *actionContentView;
@property (nonatomic, strong) UIButton *moreButton;
@property (nonatomic, strong) UIButton *parentViewsButton;
@property (nonatomic, strong) UIButton *subviewsButton;
@property (nonatomic, assign) CGFloat actionContentViewHeight;
@end
@implementation LLHierarchyInfoView
@dynamic delegate;
- (void)updateSelectedView:(UIView *)selectedView {
UIView *view = selectedView;
if (!view) {
return;
}
if (self.selectedView == view) {
return;
}
self.moreButton.enabled = YES;
self.parentViewsButton.enabled = view.superview != nil;
self.subviewsButton.enabled = view.subviews.count;
self.selectedView = view;
NSDictionary *boldAttri = @{NSFontAttributeName: [UIFont boldSystemFontOfSize:17]};
NSDictionary *attri = @{NSFontAttributeName: [UIFont systemFontOfSize:14]};
NSMutableAttributedString *name = [[NSMutableAttributedString alloc] initWithString:@"Name: " attributes:boldAttri];
[name appendAttributedString:[[NSAttributedString alloc] initWithString:NSStringFromClass(view.class) attributes:attri]];
self.contentLabel.attributedText = name;
NSMutableAttributedString *frame = [[NSMutableAttributedString alloc] initWithString:@"Frame: " attributes:boldAttri];
[frame appendAttributedString:[[NSAttributedString alloc] initWithString:[LLTool stringFromFrame:view.frame] attributes:attri]];
self.frameLabel.attributedText = frame;
if (view.backgroundColor) {
NSMutableAttributedString *color = [[NSMutableAttributedString alloc] initWithString:@"Background: " attributes:boldAttri];
[color appendAttributedString:[[NSAttributedString alloc] initWithString:[view.backgroundColor LL_description] attributes:attri]];
self.backgroundColorLabel.attributedText = color;
} else {
self.backgroundColorLabel.attributedText = nil;
}
if ([view isKindOfClass:[UILabel class]]) {
UILabel *label = (UILabel *)view;
NSMutableAttributedString *textColor = [[NSMutableAttributedString alloc] initWithString:@"Text Color: " attributes:boldAttri];
[textColor appendAttributedString:[[NSAttributedString alloc] initWithString:[label.textColor LL_description] attributes:attri]];
self.textColorLabel.attributedText = textColor;
NSMutableAttributedString *font = [[NSMutableAttributedString alloc] initWithString:@"Font: " attributes:boldAttri];
[font appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%0.2f", label.font.pointSize] attributes:attri]];
self.fontLabel.attributedText = font;
} else {
self.textColorLabel.attributedText = nil;
self.fontLabel.attributedText = nil;
}
if (view.tag != 0) {
NSMutableAttributedString *tag = [[NSMutableAttributedString alloc] initWithString:@"Tag: " attributes:boldAttri];
[tag appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%ld",(long)view.tag] attributes:attri]];
self.tagLabel.attributedText = tag;
} else {
self.tagLabel.attributedText = nil;
}
[self.contentLabel sizeToFit];
[self.frameLabel sizeToFit];
[self.backgroundColorLabel sizeToFit];
[self.textColorLabel sizeToFit];
[self.fontLabel sizeToFit];
[self.tagLabel sizeToFit];
[self updateHeightIfNeeded];
}
- (void)layoutSubviews {
[super layoutSubviews];
self.actionContentView.frame = CGRectMake(0, self.LL_height - self.actionContentViewHeight - kLLGeneralMargin, self.LL_width, self.actionContentViewHeight);
self.parentViewsButton.frame = CGRectMake(kLLGeneralMargin, 0, self.actionContentView.LL_width / 2.0 - kLLGeneralMargin * 1.5, (self.actionContentView.LL_height - kLLGeneralMargin) / 2.0);
self.subviewsButton.frame = CGRectMake(self.actionContentView.LL_width / 2.0 + kLLGeneralMargin * 0.5, self.parentViewsButton.LL_top, self.parentViewsButton.LL_width, self.parentViewsButton.LL_height);
self.moreButton.frame = CGRectMake(kLLGeneralMargin, self.parentViewsButton.LL_bottom + kLLGeneralMargin, self.actionContentView.LL_width - kLLGeneralMargin * 2, self.parentViewsButton.LL_height);
self.contentLabel.frame = CGRectMake(kLLGeneralMargin, kLLGeneralMargin, self.closeButton.LL_x - kLLGeneralMargin - kLLGeneralMargin, self.contentLabel.LL_height);
self.frameLabel.frame = CGRectMake(self.contentLabel.LL_x, self.contentLabel.LL_bottom, self.contentLabel.LL_width, self.frameLabel.LL_height);
self.backgroundColorLabel.frame = CGRectMake(self.contentLabel.LL_x, self.frameLabel.LL_bottom, self.contentLabel.LL_width, self.backgroundColorLabel.LL_height);
self.textColorLabel.frame = CGRectMake(self.contentLabel.LL_x, self.backgroundColorLabel.LL_bottom, self.contentLabel.LL_width, self.textColorLabel.LL_height);
self.fontLabel.frame = CGRectMake(self.contentLabel.LL_x, self.textColorLabel.LL_bottom, self.contentLabel.LL_width, self.fontLabel.LL_height);
self.tagLabel.frame = CGRectMake(self.contentLabel.LL_x, self.fontLabel.LL_bottom, self.contentLabel.LL_width, self.tagLabel.LL_height);
}
#pragma mark - Over write
- (void)initUI {
[super initUI];
self.actionContentViewHeight = 80;
[self addSubview:self.contentLabel];
[self addSubview:self.frameLabel];
[self addSubview:self.backgroundColorLabel];
[self addSubview:self.textColorLabel];
[self addSubview:self.fontLabel];
[self addSubview:self.tagLabel];
[self addSubview:self.actionContentView];
[self.actionContentView addSubview:self.parentViewsButton];
[self.actionContentView addSubview:self.subviewsButton];
[self.actionContentView addSubview:self.moreButton];
[self updateHeightIfNeeded];
}
#pragma mark - Event responses
- (void)buttonClicked:(UIButton *)sender {
[self.delegate LLHierarchyInfoView:self didSelectAt:sender.tag];
}
- (void)frameLabelTapGestureRecognizer:(UITapGestureRecognizer *)sender {
[self.selectedView LL_showFrameAlertAndAutomicSetWithKeyPath:@"frame"];
}
- (void)backgroundColorLabelTapGestureRecognizer:(UITapGestureRecognizer *)sender {
[self.selectedView LL_showColorAlertAndAutomicSetWithKeyPath:@"backgroundColor"];
}
- (void)textColorLabelTapGestureRecognizer:(UITapGestureRecognizer *)sender {
[self.selectedView LL_showColorAlertAndAutomicSetWithKeyPath:@"textColor"];
}
- (void)fontLabelTapGestureRecognizer:(UITapGestureRecognizer *)sender {
[self.selectedView LL_showFontAlertAndAutomicSetWithKeyPath:@"font"];
}
- (void)tagLabelTapGestureRecognizer:(UITapGestureRecognizer *)sender {
[self.selectedView LL_showIntAlertAndAutomicSetWithKeyPath:@"tag"];
}
#pragma mark - Primary
- (void)updateHeightIfNeeded {
CGFloat contentHeight = self.contentLabel.LL_height + self.frameLabel.LL_height + self.backgroundColorLabel.LL_height + self.textColorLabel.LL_height + self.fontLabel.LL_height + self.tagLabel.LL_height;
CGFloat height = kLLGeneralMargin + MAX(contentHeight, self.closeButton.LL_height) + kLLGeneralMargin + self.actionContentViewHeight + kLLGeneralMargin;
if (height != self.LL_height) {
self.LL_height = height;
if (!self.isMoved) {
if (self.LL_bottom != LL_SCREEN_HEIGHT - kLLGeneralMargin * 2) {
self.LL_bottom = LL_SCREEN_HEIGHT - kLLGeneralMargin * 2;
}
}
}
}
#pragma mark - Getters and setters
- (void)setDelegate:(id<LLHierarchyInfoViewDelegate>)delegate {
[super setDelegate:delegate];
}
- (id<LLHierarchyInfoViewDelegate>)delegate {
return (id<LLHierarchyInfoViewDelegate>)[super delegate];
}
- (UILabel *)contentLabel {
if (!_contentLabel) {
_contentLabel = [LLFactory getLabel:nil frame:CGRectZero text:nil font:14 textColor:[LLThemeManager shared].primaryColor];
_contentLabel.numberOfLines = 0;
_contentLabel.lineBreakMode = NSLineBreakByCharWrapping;
}
return _contentLabel;
}
- (UILabel *)frameLabel {
if (!_frameLabel) {
_frameLabel = [LLFactory getLabel:nil frame:CGRectZero text:nil font:14 textColor:[LLThemeManager shared].primaryColor];
_frameLabel.numberOfLines = 0;
_frameLabel.lineBreakMode = NSLineBreakByCharWrapping;
[_frameLabel LL_addClickListener:self action:@selector(frameLabelTapGestureRecognizer:)];
}
return _frameLabel;
}
- (UILabel *)backgroundColorLabel {
if (!_backgroundColorLabel) {
_backgroundColorLabel = [LLFactory getLabel:nil frame:CGRectZero text:nil font:14 textColor:[LLThemeManager shared].primaryColor];
_backgroundColorLabel.numberOfLines = 0;
_backgroundColorLabel.lineBreakMode = NSLineBreakByCharWrapping;
[_backgroundColorLabel LL_addClickListener:self action:@selector(backgroundColorLabelTapGestureRecognizer:)];
}
return _backgroundColorLabel;
}
- (UILabel *)textColorLabel {
if (!_textColorLabel) {
_textColorLabel = [LLFactory getLabel:nil frame:CGRectZero text:nil font:14 textColor:[LLThemeManager shared].primaryColor];
_textColorLabel.numberOfLines = 0;
_textColorLabel.lineBreakMode = NSLineBreakByCharWrapping;
[_textColorLabel LL_addClickListener:self action:@selector(textColorLabelTapGestureRecognizer:)];
}
return _textColorLabel;
}
- (UILabel *)fontLabel {
if (!_fontLabel) {
_fontLabel = [LLFactory getLabel:nil frame:CGRectZero text:nil font:14 textColor:[LLThemeManager shared].primaryColor];
_fontLabel.numberOfLines = 0;
_fontLabel.lineBreakMode = NSLineBreakByCharWrapping;
[_fontLabel LL_addClickListener:self action:@selector(fontLabelTapGestureRecognizer:)];
}
return _fontLabel;
}
- (UILabel *)tagLabel {
if (!_tagLabel) {
_tagLabel = [LLFactory getLabel:nil frame:CGRectZero text:nil font:14 textColor:[LLThemeManager shared].primaryColor];
_tagLabel.numberOfLines = 0;
_tagLabel.lineBreakMode = NSLineBreakByCharWrapping;
[_tagLabel LL_addClickListener:self action:@selector(tagLabelTapGestureRecognizer:)];
}
return _tagLabel;
}
- (UIView *)actionContentView {
if (!_actionContentView) {
_actionContentView = [LLFactory getView];
}
return _actionContentView;
}
- (UIButton *)parentViewsButton {
if (!_parentViewsButton) {
_parentViewsButton = [LLFactory getButton:nil frame:CGRectZero target:self action:@selector(buttonClicked:)];
[_parentViewsButton setTitle:LLLocalizedString(@"hierarchy.parent") forState:UIControlStateNormal];
[_parentViewsButton setTitleColor:[LLThemeManager shared].primaryColor forState:UIControlStateNormal];
_parentViewsButton.titleLabel.font = [UIFont systemFontOfSize:14];
_parentViewsButton.backgroundColor = [LLThemeManager shared].backgroundColor;
[_parentViewsButton LL_setBorderColor:[LLThemeManager shared].primaryColor borderWidth:1];
[_parentViewsButton LL_setCornerRadius:5];
_parentViewsButton.tintColor = [LLThemeManager shared].primaryColor;
_parentViewsButton.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, kLLGeneralMargin);
[_parentViewsButton setImage:[[UIImage LL_imageNamed:kParentImageName] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
_parentViewsButton.tag = LLHierarchyInfoViewActionShowParent;
_parentViewsButton.enabled = NO;
}
return _parentViewsButton;
}
- (UIButton *)subviewsButton {
if (!_subviewsButton) {
_subviewsButton = [LLFactory getButton:nil frame:CGRectZero target:self action:@selector(buttonClicked:)];
[_subviewsButton setTitle:LLLocalizedString(@"hierarchy.subview") forState:UIControlStateNormal];
[_subviewsButton setTitleColor:[LLThemeManager shared].primaryColor forState:UIControlStateNormal];
_subviewsButton.titleLabel.font = [UIFont systemFontOfSize:14];
_subviewsButton.backgroundColor = [LLThemeManager shared].backgroundColor;
[_subviewsButton LL_setBorderColor:[LLThemeManager shared].primaryColor borderWidth:1];
[_subviewsButton LL_setCornerRadius:5];
_subviewsButton.tintColor = [LLThemeManager shared].primaryColor;
_subviewsButton.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, kLLGeneralMargin);
[_subviewsButton setImage:[[UIImage LL_imageNamed:kSubviewImageName] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
_subviewsButton.tag = LLHierarchyInfoViewActionShowSubview;
_subviewsButton.enabled = NO;
}
return _subviewsButton;
}
- (UIButton *)moreButton {
if (!_moreButton) {
_moreButton = [LLFactory getButton:nil frame:CGRectZero target:self action:@selector(buttonClicked:)];
[_moreButton setTitle:LLLocalizedString(@"hierarchy.more") forState:UIControlStateNormal];
[_moreButton setTitleColor:[LLThemeManager shared].primaryColor forState:UIControlStateNormal];
_moreButton.titleLabel.font = [UIFont systemFontOfSize:14];
_moreButton.backgroundColor = [LLThemeManager shared].backgroundColor;
[_moreButton LL_setBorderColor:[LLThemeManager shared].primaryColor borderWidth:1];
[_moreButton LL_setCornerRadius:5];
_moreButton.tintColor = [LLThemeManager shared].primaryColor;
_moreButton.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, kLLGeneralMargin);
[_moreButton setImage:[[UIImage LL_imageNamed:kInfoImageName] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal];
_moreButton.tag = LLHierarchyInfoViewActionShowMoreInfo;
_moreButton.enabled = NO;
}
return _moreButton;
}
@end

View File

@@ -0,0 +1,45 @@
//
// LLHierarchyPickerView.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLPickerView.h"
NS_ASSUME_NONNULL_BEGIN
@class LLHierarchyPickerView;
/// Hierarchy picker view delegate.
@protocol LLHierarchyPickerViewDelegate <NSObject>
- (void)LLHierarchyPickerView:(LLHierarchyPickerView *)view didMoveTo:(NSArray <UIView *>*)selectedViews;
@end
/// Hierarchy picker view.
@interface LLHierarchyPickerView : LLPickerView
/// Action delegate.
@property (nonatomic, weak, nullable) id<LLHierarchyPickerViewDelegate> delegate;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,85 @@
//
// LLHierarchyPickerView.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLHierarchyPickerView.h"
#import "LLHierarchyHelper.h"
#import "LLThemeManager.h"
#import "LLBaseWindow.h"
#import "LLConfig.h"
#import "UIView+LL_Utils.h"
@implementation LLHierarchyPickerView
#pragma mark - Over write
- (void)viewDidUpdateOffset:(UIPanGestureRecognizer *)sender offset:(CGPoint)offsetPoint {
NSArray <UIView *>*views = [self viewForSelectionAtPoint:self.center];
[self.delegate LLHierarchyPickerView:self didMoveTo:views];
}
#pragma mark - Primary
- (NSArray<UIView *> *)viewForSelectionAtPoint:(CGPoint)tapPointInWindow
{
// Select in the window that would handle the touch, but don't just use the result of hitTest:withEvent: so we can still select views with interaction disabled.
// Default to the the application's key window if none of the windows want the touch.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
UIWindow *windowForSelection = [[UIApplication sharedApplication] keyWindow];
#pragma clang diagnostic pop
for (UIWindow *window in [[[LLHierarchyHelper shared] allWindowsIgnoreClass:[LLBaseWindow class]] reverseObjectEnumerator]) {
if ([window hitTest:tapPointInWindow withEvent:nil]) {
windowForSelection = window;
break;
}
}
// Select the deepest visible view at the tap point. This generally corresponds to what the user wants to select.
return [self recursiveSubviewsAtPoint:tapPointInWindow inView:windowForSelection skipHiddenViews:YES];
}
- (NSArray<UIView *> *)recursiveSubviewsAtPoint:(CGPoint)pointInView inView:(UIView *)view skipHiddenViews:(BOOL)skipHidden
{
NSMutableArray<UIView *> *subviewsAtPoint = [NSMutableArray array];
for (UIView *subview in view.subviews) {
BOOL isHidden = subview.hidden || subview.alpha < 0.01;
if (skipHidden && isHidden) {
continue;
}
BOOL subviewContainsPoint = CGRectContainsPoint(subview.frame, pointInView);
if (subviewContainsPoint) {
[subviewsAtPoint addObject:subview];
}
// If this view doesn't clip to its bounds, we need to check its subviews even if it doesn't contain the selection point.
// They may be visible and contain the selection point.
if (subviewContainsPoint || !subview.clipsToBounds) {
CGPoint pointInSubview = [view convertPoint:pointInView toView:subview];
[subviewsAtPoint addObjectsFromArray:[self recursiveSubviewsAtPoint:pointInSubview inView:subview skipHiddenViews:skipHidden]];
}
}
return subviewsAtPoint;
}
@end

View File

@@ -0,0 +1,33 @@
//
// LLHierarchyViewController.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLBaseComponentViewController.h"
NS_ASSUME_NONNULL_BEGIN
/// Hierarchy function view controller.
@interface LLHierarchyViewController : LLBaseComponentViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,271 @@
//
// LLHierarchyViewController.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLHierarchyViewController.h"
#import "LLHierarchyDetailViewController.h"
#import "LLNavigationController.h"
#import "LLHierarchyInfoView.h"
#import "LLHierarchyPickerView.h"
#import "LLInternalMacros.h"
#import "LLWindowManager.h"
#import "LLThemeManager.h"
#import "LLFactory.h"
#import "LLConfig.h"
#import "LLConst.h"
#import "LLTool.h"
#import "UIViewController+LL_Utils.h"
#import "NSObject+LL_Utils.h"
#import "UIView+LL_Utils.h"
@interface LLHierarchyViewController ()<LLHierarchyPickerViewDelegate, LLHierarchyInfoViewDelegate>
@property (nonatomic, strong) UIView *borderView;
@property (nonatomic, strong) LLHierarchyPickerView *pickerView;
@property (nonatomic, strong) LLHierarchyInfoView *infoView;
@property (nonatomic, strong) NSMutableSet *observeViews;
@property (nonatomic, strong) NSMutableDictionary *borderViews;
@end
@implementation LLHierarchyViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.1];
self.observeViews = [NSMutableSet set];
self.borderViews = [[NSMutableDictionary alloc] init];
CGFloat height = 100;
self.infoView = [[LLHierarchyInfoView alloc] initWithFrame:CGRectMake(kLLGeneralMargin, LL_SCREEN_HEIGHT - kLLGeneralMargin * 2 - height, LL_SCREEN_WIDTH - kLLGeneralMargin * 2, height)];
self.infoView.delegate = self;
[self.view addSubview:self.infoView];
[self.view addSubview:self.borderView];
self.pickerView = [[LLHierarchyPickerView alloc] initWithFrame:CGRectMake((self.view.LL_width - 60) / 2.0, (self.view.LL_height - 60) / 2.0, 60, 60)];
self.pickerView.delegate = self;
[self.view addSubview:self.pickerView];
}
- (void)dealloc {
for (UIView *view in self.observeViews) {
[self stopObserveView:view];
}
[self.observeViews removeAllObjects];
}
#pragma mark - Primary
- (void)beginObserveView:(UIView *)view borderWidth:(CGFloat)borderWidth {
if ([self.observeViews containsObject:view]) {
return;
}
UIView *borderView = [LLFactory getView];
borderView.backgroundColor = [UIColor clearColor];
[self.view addSubview:borderView];
[self.view sendSubviewToBack:borderView];
[borderView LL_setBorderColor:view.LL_hashColor borderWidth:borderWidth];
borderView.frame = [self frameInLocalForView:view];
[self.borderViews setObject:borderView forKey:@(view.hash)];
[view addObserver:self forKeyPath:@"frame" options:0 context:NULL];
}
- (void)stopObserveView:(UIView *)view {
if (![self.observeViews containsObject:view]) {
return;
}
UIView *borderView = self.borderViews[@(view.hash)];
[borderView removeFromSuperview];
[view removeObserver:self forKeyPath:@"frame"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *, id> *)change context:(void *)context {
if ([object isKindOfClass:[UIView class]]) {
UIView *view = (UIView *)object;
[self updateOverlayIfNeeded:view];
}
}
- (void)updateOverlayIfNeeded:(UIView *)view {
UIView *borderView = self.borderViews[@(view.hash)];
if (borderView) {
borderView.frame = [self frameInLocalForView:view];
}
}
- (CGRect)frameInLocalForView:(UIView *)view {
UIWindow *window = [UIApplication sharedApplication].delegate.window;
CGRect rect = [view convertRect:view.bounds toView:window];
rect = [self.view convertRect:rect fromView:window];
return rect;
}
- (UIView *)findSelectedViewInViews:(NSArray *)selectedViews {
if ([LLConfig shared].isHierarchyIgnorePrivateClass) {
NSMutableArray *views = [[NSMutableArray alloc] init];
for (UIView *view in selectedViews) {
if (![NSStringFromClass(view.class) hasPrefix:@"_"]) {
[views addObject:view];
}
}
return [views lastObject];
} else {
return [selectedViews lastObject];
}
}
- (NSArray <UIView *>*)findParentViewsBySelectedView:(UIView *)selectedView {
NSMutableArray *views = [[NSMutableArray alloc] init];
UIView *view = [selectedView superview];
while (view) {
if ([LLConfig shared].isHierarchyIgnorePrivateClass) {
if (![NSStringFromClass(view.class) hasPrefix:@"_"]) {
[views addObject:view];
}
} else {
[views addObject:view];
}
view = view.superview;
}
return [views copy];
}
- (NSArray <UIView *>*)findSubviewsBySelectedView:(UIView *)selectedView {
NSMutableArray *views = [[NSMutableArray alloc] init];
for (UIView *view in selectedView.subviews) {
if ([LLConfig shared].isHierarchyIgnorePrivateClass) {
if (![NSStringFromClass(view.class) hasPrefix:@"_"]) {
[views addObject:view];
}
} else {
[views addObject:view];
}
}
return [views copy];
}
#pragma mark - LLHierarchyPickerViewDelegate
- (void)LLHierarchyPickerView:(LLHierarchyPickerView *)view didMoveTo:(NSArray <UIView *>*)selectedViews {
@synchronized (self) {
for (UIView *view in self.observeViews) {
[self stopObserveView:view];
}
[self.observeViews removeAllObjects];
for (NSInteger i = selectedViews.count - 1; i >= 0; i--) {
UIView *view = selectedViews[i];
CGFloat borderWidth = 1;
if (i == selectedViews.count - 1) {
borderWidth = 2;
}
[self beginObserveView:view borderWidth:borderWidth];
}
[self.observeViews addObjectsFromArray:selectedViews];
}
[self.infoView updateSelectedView:[self findSelectedViewInViews:selectedViews]];
}
#pragma mark - LLBaseInfoViewDelegate
- (void)LLInfoViewDidSelectCloseButton:(LLInfoView *)view {
[self componentDidLoad:nil];
}
#pragma mark - LLHierarchyInfoViewDelegate
- (void)LLHierarchyInfoView:(LLHierarchyInfoView *)view didSelectAt:(LLHierarchyInfoViewAction)action {
UIView *selectView = self.infoView.selectedView;
if (selectView == nil) {
[LLTool log:@"Failed to show hierarchy detail viewController"];
return;
}
switch (action) {
case LLHierarchyInfoViewActionShowMoreInfo:{
[self showHierarchyInfo:selectView];
}
break;
case LLHierarchyInfoViewActionShowParent: {
[self showParentSheet:selectView];
}
break;
case LLHierarchyInfoViewActionShowSubview: {
[self showSubviewSheet:selectView];
}
break;
}
}
- (void)showHierarchyInfo:(UIView *)selectView {
LLHierarchyDetailViewController *vc = [[LLHierarchyDetailViewController alloc] init];
vc.selectView = selectView;
LLNavigationController *nav = [[LLNavigationController alloc] initWithRootViewController:vc];
[self presentViewController:nav animated:YES completion:nil];
}
- (void)showParentSheet:(UIView *)selectView {
NSMutableArray *actions = [[NSMutableArray alloc] init];
__block NSArray *parentViews = [self findParentViewsBySelectedView:selectView];
for (UIView *view in parentViews) {
[actions addObject:NSStringFromClass(view.class)];
}
__weak typeof(self) weakSelf = self;
[self LL_showActionSheetWithTitle:LLLocalizedString(@"hierarchy.parent") actions:actions currentAction:nil completion:^(NSInteger index) {
[weakSelf setNewSelectView:parentViews[index]];
}];
}
- (void)showSubviewSheet:(UIView *)selectView {
NSMutableArray *actions = [[NSMutableArray alloc] init];
__block NSArray *subviews = [self findSubviewsBySelectedView:selectView];
for (UIView *view in subviews) {
[actions addObject:NSStringFromClass(view.class)];
}
__weak typeof(self) weakSelf = self;
[self LL_showActionSheetWithTitle:LLLocalizedString(@"hierarchy.subview") actions:actions currentAction:nil completion:^(NSInteger index) {
[weakSelf setNewSelectView:subviews[index]];
}];
}
- (void)setNewSelectView:(UIView *)view {
[self LLHierarchyPickerView:self.pickerView didMoveTo:@[view]];
}
#pragma mark - Getters and setters
- (UIView *)borderView {
if (!_borderView) {
_borderView = [LLFactory getView];
_borderView.backgroundColor = [UIColor clearColor];
_borderView.layer.borderWidth = 2;
}
return _borderView;
}
@end

View File

@@ -0,0 +1,33 @@
//
// LLHierarchyWindow.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLComponentWindow.h"
NS_ASSUME_NONNULL_BEGIN
/// Hierarchy function window.
@interface LLHierarchyWindow : LLComponentWindow
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,48 @@
//
// LLHierarchyWindow.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLHierarchyWindow.h"
#import "LLHierarchyViewController.h"
#import "LLWindowManager.h"
@interface LLHierarchyWindow ()
@end
@implementation LLHierarchyWindow
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
if (!self.rootViewController) {
self.rootViewController = [[LLHierarchyViewController alloc] init];
}
}
return self;
}
- (void)componentDidFinish {
[[LLWindowManager shared] showEntryWindow];
}
@end

View File

@@ -0,0 +1,41 @@
//
// LLWindowManager+Hierarchy.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLWindowManager.h"
#import "LLHierarchyWindow.h"
NS_ASSUME_NONNULL_BEGIN
@interface LLWindowManager (Hierarchy)
/**
Hierarchy picker window
@return LLHierarchyWindow.
*/
+ (LLHierarchyWindow *)hierarchyWindow;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,32 @@
//
// LLWindowManager+Hierarchy.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLWindowManager+Hierarchy.h"
@implementation LLWindowManager (Hierarchy)
+ (LLHierarchyWindow *)hierarchyWindow {
return (LLHierarchyWindow *)[self createWindowWithClassName:NSStringFromClass([LLHierarchyWindow class])];
}
@end

View File

@@ -0,0 +1,37 @@
//
// LLHtml.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef LLHtml_h
#define LLHtml_h
#import "LLHtmlComponent.h"
#import "LLHtmlConfigViewController.h"
#import "LLHtmlUIWebViewController.h"
#import "LLHtmlWkWebViewController.h"
#import "LLHtmlViewController.h"
#import "LLHtmlWindow.h"
#import "LLWindowManager+Html.h"
#endif /* LLHtml_h */

View File

@@ -0,0 +1,35 @@
//
// LLHtmlComponent.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLComponent.h"
#import "LLHtmlWindow.h"
NS_ASSUME_NONNULL_BEGIN
/// Html function component.
@interface LLHtmlComponent : LLComponent
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,61 @@
//
// LLHtmlComponent.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLHtmlComponent.h"
#import "LLHtmlConfigViewController.h"
#import "LLNavigationController.h"
#import "LLWindowManager+Html.h"
@implementation LLHtmlComponent
- (void)componentDidLoad:(NSDictionary<NSString *,id> *)data {
if (data[kLLComponentWindowRootViewControllerKey]) {
Class rootViewControllerClass = NSClassFromString(data[kLLComponentWindowRootViewControllerKey]);
if (rootViewControllerClass != nil) {
UIViewController *viewController = [[rootViewControllerClass alloc] init];
NSDictionary *properties = data[kLLComponentWindowRootViewControllerPropertiesKey];
for (NSString *key in properties) {
id value = properties[key];
[viewController setValue:value forKey:key];
}
LLNavigationController *nav = [[LLNavigationController alloc] initWithRootViewController:viewController];
LLHtmlWindow *window = [LLWindowManager htmlWindow];
window.rootViewController = nav;
[[LLWindowManager shared] showWindow:window animated:YES];
return;
}
}
LLBaseWindow *window = [[LLWindowManager shared] visiableWindow];
if ([window isKindOfClass:[LLFunctionWindow class]]) {
LLNavigationController *nav = (LLNavigationController *)window.rootViewController;
[nav pushViewController:[[LLHtmlConfigViewController alloc] init] animated:YES];
} else {
[[LLWindowManager shared] showWindow:[LLWindowManager htmlWindow] animated:YES];
}
}
@end

View File

@@ -0,0 +1,33 @@
//
// LLHtmlConfigViewController.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLTitleViewController.h"
NS_ASSUME_NONNULL_BEGIN
/// Html config view controller.
@interface LLHtmlConfigViewController : LLTitleViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,221 @@
//
// LLHtmlConfigViewController.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLHtmlConfigViewController.h"
#import <WebKit/WebKit.h>
#import "LLHtmlUIWebViewController.h"
#import "LLHtmlWkWebViewController.h"
#import "LLTitleCellCategoryModel.h"
#import "LLHtmlViewController.h"
#import "LLSettingManager.h"
#import "LLTitleCellModel.h"
#import "LLInternalMacros.h"
#import "LLThemeManager.h"
#import "LLToastUtils.h"
#import "LLFactory.h"
#import "LLConfig.h"
#import "LLConst.h"
#import "UIViewController+LL_Utils.h"
#import "UIView+LL_Utils.h"
@interface LLHtmlConfigViewController () <UITextFieldDelegate>
@property (nonatomic, copy) NSString *webViewClass;
@property (nonatomic, strong) UIView *headerView;
@property (nonatomic, strong) UITextField *headerTextField;
@end
@implementation LLHtmlConfigViewController
#pragma mark - Life cycle
- (void)viewDidLoad {
[super viewDidLoad];
[self setUpUI];
[self loadData];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
if ([self.headerTextField isFirstResponder]) {
[self.headerTextField resignFirstResponder];
}
}
#pragma mark - Over write
- (void)rightItemClick:(UIButton *)sender {
NSString *urlString = [self currentUrlString];
if (!urlString) {
[[LLToastUtils shared] toastMessage:@"Empty URL"];
return;
}
if (![urlString.lowercaseString hasPrefix:@"https://"] && ![urlString.lowercaseString hasPrefix:@"http://"]) {
[[LLToastUtils shared] toastMessage:LLLocalizedString(@"html.url.check")];
return;
}
Class cls = NSClassFromString(self.webViewClass);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if (cls != [UIWebView class] && cls != [WKWebView class]) {
#pragma clang diagnostic pop
if ([LLConfig shared].htmlViewControllerProvider != nil) {
UIViewController *customViewController = [LLConfig shared].htmlViewControllerProvider(urlString);
if (customViewController && cls == [customViewController class]) {
[LLSettingManager shared].lastWebViewUrl = urlString;
[self.navigationController pushViewController:customViewController animated:YES];
return;
}
[[LLToastUtils shared] toastMessage:LLLocalizedString(@"html.provider.fail")];
return;
}
[[LLToastUtils shared] toastMessage:LLLocalizedString(@"html.invalid.class")];
return;
}
[LLSettingManager shared].lastWebViewUrl = urlString;
LLHtmlViewController *vc = nil;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if (cls == [UIWebView class]) {
#pragma clang diagnostic pop
vc = [[LLHtmlUIWebViewController alloc] init];
} else {
vc = [[LLHtmlWkWebViewController alloc] init];
}
vc.webViewClass = self.webViewClass;
vc.urlString = [self currentUrlString];
[self.navigationController pushViewController:vc animated:YES];
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
#pragma mark - Primary
- (void)setUpUI {
self.title = LLLocalizedString(@"function.html");
[self initNavigationItemWithTitle:@"Go" imageName:nil isLeft:NO];
self.webViewClass = [LLSettingManager shared].webViewClass ?: NSStringFromClass([WKWebView class]);
self.tableView.tableHeaderView = self.headerView;
}
- (void)loadData {
NSMutableArray *settings = [[NSMutableArray alloc] init];
// Short Cut
[settings addObject:[self getWebViewStyleModel]];
LLTitleCellCategoryModel *category0 = [[LLTitleCellCategoryModel alloc] initWithTitle:nil items:settings];
[settings removeAllObjects];
[self.dataArray removeAllObjects];
[self.dataArray addObjectsFromArray:@[category0]];
[self.tableView reloadData];
}
- (LLTitleCellModel *)getWebViewStyleModel {
LLTitleCellModel *model = [[LLTitleCellModel alloc] initWithTitle:LLLocalizedString(@"style") detailTitle:self.webViewClass];
__weak typeof(self) weakSelf = self;
model.block = ^{
[weakSelf showWebViewClassAlert];
};
return model;
}
- (void)showWebViewClassAlert {
__block NSMutableArray *actions = [[NSMutableArray alloc] init];
[actions addObject:NSStringFromClass([WKWebView class])];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[actions addObject:NSStringFromClass([UIWebView class])];
#pragma clang diagnostic pop
if ([LLConfig shared].htmlViewControllerProvider != nil) {
UIViewController *vc = [LLConfig shared].htmlViewControllerProvider(nil);
if (vc) {
[actions addObject:NSStringFromClass([vc class])];
}
}
__weak typeof(self) weakSelf = self;
[self LL_showActionSheetWithTitle:LLLocalizedString(@"style") actions:actions currentAction:self.webViewClass completion:^(NSInteger index) {
[weakSelf setNewWebViewClass:actions[index]];
}];
}
- (void)setNewWebViewClass:(NSString *)aClass {
self.webViewClass = aClass;
[LLSettingManager shared].webViewClass = aClass;
[self loadData];
}
- (NSString *)currentUrlString {
NSString *text = self.headerTextField.text;
text = [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (!text || text.length == 0) {
return nil;
}
return text;
}
#pragma mark - Getters and setters
- (UIView *)headerView {
if (!_headerView) {
_headerView = [LLFactory getView];
_headerView.frame = CGRectMake(0, 0, LL_SCREEN_WIDTH, 60);
[_headerView addSubview:self.headerTextField];
self.headerTextField.frame = CGRectMake(kLLGeneralMargin, kLLGeneralMargin, _headerView.LL_width - kLLGeneralMargin * 2, _headerView.LL_height - kLLGeneralMargin * 2);
}
return _headerView;
}
- (UITextField *)headerTextField {
if (!_headerTextField) {
_headerTextField = [LLFactory getTextField];
_headerTextField.tintColor = [LLThemeManager shared].primaryColor;
_headerTextField.backgroundColor = [LLThemeManager shared].backgroundColor;
[_headerTextField LL_setBorderColor:[LLThemeManager shared].primaryColor borderWidth:1];
[_headerTextField LL_setCornerRadius:5];
_headerTextField.font = [UIFont systemFontOfSize:14];
_headerTextField.textColor = [LLThemeManager shared].primaryColor;
_headerTextField.delegate = self;
_headerTextField.clearButtonMode = UITextFieldViewModeWhileEditing;
_headerTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:LLLocalizedString(@"html.input.url") attributes:@{NSForegroundColorAttributeName : [LLThemeManager shared].placeHolderColor}];
_headerTextField.text = [LLSettingManager shared].lastWebViewUrl ?: ([LLConfig shared].defaultHtmlUrl ?: @"https://");
UIView *leftView = [LLFactory getView];
leftView.frame = CGRectMake(0, 0, 10, 1);
_headerTextField.leftView = leftView;
_headerTextField.leftViewMode = UITextFieldViewModeAlways;
}
return _headerTextField;
}
@end

View File

@@ -0,0 +1,33 @@
//
// LLHtmlUIWebViewController.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLHtmlViewController.h"
NS_ASSUME_NONNULL_BEGIN
/// Html UIWebView controller.
@interface LLHtmlUIWebViewController : LLHtmlViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,106 @@
//
// LLHtmlUIWebViewController.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLHtmlUIWebViewController.h"
#import "LLTool.h"
@interface LLHtmlUIWebViewController () <UIWebViewDelegate>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
@property (nonatomic, strong) UIWebView *webView;
#pragma clang diagnostic pop
@end
@implementation LLHtmlUIWebViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setUpUI];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.webView.frame = self.view.bounds;
}
#pragma mark - UIWebViewDelegate
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (void)webViewDidStartLoad:(UIWebView *)webView {
[LLTool log:[NSString stringWithFormat:@"UIWebView start load %@",self.urlString]];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[LLTool log:[NSString stringWithFormat:@"UIWebView finish load %@",self.urlString]];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
[LLTool log:[NSString stringWithFormat:@"UIWebView failed in %@, with error %@", self.urlString, error.debugDescription]];
}
#pragma clang diagnostic pop
#pragma mark - Primary
- (void)setUpUI {
if (![self webViewClassIsValid]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
self.webViewClass = NSStringFromClass([UIWebView class]);
#pragma clang diagnostic pop
}
[self.view addSubview:self.webView];
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.urlString]]];
}
- (BOOL)webViewClassIsValid {
if (!self.webViewClass || self.webViewClass.length == 0) {
return NO;
}
Class cls = NSClassFromString(self.webViewClass);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if (cls != [UIWebView class]) {
#pragma clang diagnostic pop
return NO;
}
return YES;
}
#pragma mark - Getters and setters
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (UIWebView *)webView {
if (!_webView) {
_webView = (UIWebView *)[[NSClassFromString(self.webViewClass) alloc] init];
_webView.delegate = self;
}
return _webView;
}
#pragma clang diagnostic pop
@end

View File

@@ -0,0 +1,39 @@
//
// LLHtmlViewController.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLBaseComponentViewController.h"
NS_ASSUME_NONNULL_BEGIN
/// Html view controller.
@interface LLHtmlViewController : LLBaseComponentViewController
/// Web view class.
@property (nonatomic, copy) NSString *webViewClass;
/// Target url.
@property (nonatomic, copy) NSString *urlString;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,45 @@
//
// LLHtmlViewController.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLHtmlViewController.h"
#import "LLTool.h"
@interface LLHtmlViewController ()
@end
@implementation LLHtmlViewController
- (void)viewDidLoad {
[super viewDidLoad];
if (!self.urlString || self.urlString.length == 0) {
self.urlString = @"https://github.com/HDB-Li/LLDebugTool";
[LLTool log:@"None url string"];
}
NSURL *url = [NSURL URLWithString:self.urlString];
self.title = url.host ?: self.urlString;
}
@end

View File

@@ -0,0 +1,33 @@
//
// LLHtmlWindow.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLComponentWindow.h"
NS_ASSUME_NONNULL_BEGIN
/// Html function window.
@interface LLHtmlWindow : LLComponentWindow
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,52 @@
//
// LLHtmlWindow.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLHtmlWindow.h"
#import "LLComponentNavigationController.h"
#import "LLHtmlConfigViewController.h"
#import "LLWindowManager.h"
#import "LLConfig.h"
@implementation LLHtmlWindow
#pragma mark - Life cycle
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.showAnimateStyle = LLBaseWindowShowAnimateStylePresent;
self.hideAnimateStyle = LLBaseWindowHideAnimateStyleDismiss;
if (!self.rootViewController) {
LLHtmlConfigViewController *vc = [[LLHtmlConfigViewController alloc] init];
LLNavigationController *nav = [[LLNavigationController alloc] initWithRootViewController:vc];
self.rootViewController = nav;
}
}
return self;
}
#pragma mark - Over write
- (void)componentDidFinish {
[[LLWindowManager shared] showEntryWindow];
}
@end

View File

@@ -0,0 +1,33 @@
//
// LLHtmlWkWebViewController.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLHtmlViewController.h"
NS_ASSUME_NONNULL_BEGIN
/// Html WKWebView controller.
@interface LLHtmlWkWebViewController : LLHtmlViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,134 @@
//
// LLHtmlWkWebViewController.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLHtmlWkWebViewController.h"
#import <WebKit/WebKit.h>
#import "UIViewController+LL_Utils.h"
@interface LLHtmlWkWebViewController () <WKNavigationDelegate, WKUIDelegate>
@property (nonatomic, strong) WKWebView *webView;
@property (nonatomic, strong) WKWebViewConfiguration *webViewConfiguration;
@property (nonatomic, strong) WKPreferences *preferences;
@end
@implementation LLHtmlWkWebViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setUpUI];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.webView.frame = self.view.bounds;
}
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
NSLog(@"%@",NSStringFromSelector(_cmd));
}
#pragma mark - WKUIDelegate
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
[self LL_showConfirmAlertControllerWithMessage:message handler:^{
completionHandler();
}];
}
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
[self LL_showAlertControllerWithMessage:message handler:^(NSInteger action) {
completionHandler(action == 0 ? NO : YES);
}];
}
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
[self LL_showTextFieldAlertControllerWithMessage:prompt text:defaultText handler:^(NSString * _Nullable newText) {
completionHandler(newText);
}];
}
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
return nil;
}
#pragma mark - Primary
- (void)setUpUI {
if (![self webViewClassIsValid]) {
self.webViewClass = NSStringFromClass([WKWebView class]);
}
[self.view addSubview:self.webView];
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.urlString]]];
}
- (BOOL)webViewClassIsValid {
if (!self.webViewClass || self.webViewClass.length == 0) {
return NO;
}
Class cls = NSClassFromString(self.webViewClass);
if (cls != [WKWebView class]) {
return NO;
}
return YES;
}
#pragma mark - Getters and setters
- (WKWebView *)webView {
if (!_webView) {
_webView = (WKWebView *)[[NSClassFromString(self.webViewClass) alloc] initWithFrame:CGRectZero configuration:self.webViewConfiguration];
_webView.UIDelegate = self;
_webView.navigationDelegate = self;
}
return _webView;
}
- (WKWebViewConfiguration *)webViewConfiguration {
if (!_webViewConfiguration) {
_webViewConfiguration = [[WKWebViewConfiguration alloc] init];
_webViewConfiguration.preferences = self.preferences;
}
return _webViewConfiguration;
}
- (WKPreferences *)preferences {
if (!_preferences) {
_preferences = [[WKPreferences alloc] init];
_preferences.javaScriptCanOpenWindowsAutomatically = YES;
}
return _preferences;
}
@end

View File

@@ -0,0 +1,41 @@
//
// LLWindowManager+Html.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLWindowManager.h"
#import "LLHtmlWindow.h"
NS_ASSUME_NONNULL_BEGIN
@interface LLWindowManager (Html)
/**
Html window.
@return LLHtmlWindow.
*/
+ (LLHtmlWindow *)htmlWindow;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,32 @@
//
// LLWindowManager+Html.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLWindowManager+Html.h"
@implementation LLWindowManager (Html)
+ (LLHtmlWindow *)htmlWindow {
return (LLHtmlWindow *)[self createWindowWithClassName:NSStringFromClass([LLHtmlWindow class])];
}
@end

View File

@@ -0,0 +1,36 @@
//
// CLLocation+LL_Location.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <CoreLocation/CoreLocation.h>
NS_ASSUME_NONNULL_BEGIN
@interface CLLocation (LL_Location)
/// Whether is mock
@property (nonatomic, assign, getter=LL_isMock) BOOL LL_mock;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,38 @@
//
// CLLocation+LL_Location.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "CLLocation+LL_Location.h"
#import "NSObject+LL_Runtime.h"
@implementation CLLocation (LL_Location)
- (void)setLL_mock:(BOOL)LL_mock {
objc_setAssociatedObject(self, @selector(LL_isMock), @(LL_mock), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)LL_isMock {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
@end

View File

@@ -0,0 +1,43 @@
//
// CLLocationManager+LL_Location.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <CoreLocation/CoreLocation.h>
@class LLLocationProxy;
NS_ASSUME_NONNULL_BEGIN
FOUNDATION_EXPORT NSNotificationName const LLCLLocationRegisterNotificationName;
FOUNDATION_EXPORT NSNotificationName const LLCLLocationUnRegisterNotificationName;
@interface CLLocationManager (LL_Location)
/// Delegate proxy.
@property (nonatomic, strong, nullable) LLLocationProxy *LL_delegateProxy;
@property (nonatomic, assign) BOOL LL_isUpdatingLocation;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,105 @@
//
// CLLocationManager+LL_Location.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "CLLocationManager+LL_Location.h"
#import "LLLocationProxy.h"
#import "NSObject+LL_Runtime.h"
NSNotificationName const LLCLLocationRegisterNotificationName = @"LLCLLocationRegisterNotificationName";
NSNotificationName const LLCLLocationUnRegisterNotificationName = @"LLCLLocationUnRegisterNotificationName";
@implementation CLLocationManager (LL_Location)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self LL_swizzleInstanceMethodWithOriginSel:@selector(setDelegate:) swizzledSel:@selector(LL_setDelegate:)];
[self LL_swizzleInstanceMethodWithOriginSel:@selector(delegate) swizzledSel:@selector(LL_delegate)];
[self LL_swizzleInstanceMethodWithOriginSel:@selector(init) swizzledSel:@selector(LL_init)];
[self LL_swizzleInstanceMethodWithOriginSel:NSSelectorFromString(@"dealloc") swizzledSel:@selector(LL_dealloc)];
[self LL_swizzleInstanceMethodWithOriginSel:@selector(startUpdatingLocation) swizzledSel:@selector(LL_startUpdatingLocation)];
[self LL_swizzleInstanceMethodWithOriginSel:@selector(stopUpdatingLocation) swizzledSel:@selector(LL_stopUpdatingLocation)];
});
}
- (instancetype)LL_init {
CLLocationManager *manager = [self LL_init];
[[NSNotificationCenter defaultCenter] postNotificationName:LLCLLocationRegisterNotificationName object:manager];
return manager;
}
- (void)LL_setDelegate:(id<CLLocationManagerDelegate>)delegate {
if (delegate) {
LLLocationProxy *proxy = [[LLLocationProxy alloc] initWithTarget:delegate];
self.LL_delegateProxy = proxy;
[self LL_setDelegate:proxy];
} else {
self.LL_delegateProxy = nil;
[self LL_setDelegate:delegate];
}
}
- (id<CLLocationManagerDelegate>)LL_delegate {
id delegate = [self LL_delegate];
if (delegate == self.LL_delegateProxy) {
return self.LL_delegateProxy.target;
}
return delegate;
}
- (void)LL_dealloc {
[[NSNotificationCenter defaultCenter] postNotificationName:LLCLLocationUnRegisterNotificationName object:self];
[self LL_dealloc];
}
- (void)LL_startUpdatingLocation {
self.LL_isUpdatingLocation = YES;
[self LL_startUpdatingLocation];
}
- (void)LL_stopUpdatingLocation {
self.LL_isUpdatingLocation = NO;
[self LL_stopUpdatingLocation];
}
#pragma mark - Getters and setters
- (void)setLL_delegateProxy:(LLLocationProxy *)LL_delegateProxy {
objc_setAssociatedObject(self, @selector(LL_delegateProxy), LL_delegateProxy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (LLLocationProxy *)LL_delegateProxy {
return objc_getAssociatedObject(self, _cmd);
}
- (void)setLL_isUpdatingLocation:(BOOL)LL_isUpdatingLocation {
objc_setAssociatedObject(self, @selector(LL_isUpdatingLocation), @(LL_isUpdatingLocation), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)LL_isUpdatingLocation {
return [objc_getAssociatedObject(self, _cmd) boolValue];
}
@end

View File

@@ -0,0 +1,98 @@
//
// LLLocationHelper.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <CoreLocation/CoreLocation.h>
@class LLLocationMockRouteModel;
NS_ASSUME_NONNULL_BEGIN
/// Location helper.
@interface LLLocationHelper : NSObject
/// Shared instance.
+ (instancetype)shared;
/**
Set enable to monitoring network request.
*/
@property (nonatomic, assign, getter=isEnabled) BOOL enable;
/**
Is Mock route.
*/
@property (nonatomic, assign, readonly) BOOL isMockRoute;
/**
Available route models.
*/
@property (nonatomic, copy) NSArray <LLLocationMockRouteModel *>*availableRoutes;
/**
Add a custom mock route file.
*/
- (void)addMockRouteFile:(NSString *)filePath;
/**
Add all json file in directory, deep find.
*/
- (void)addMockRouteDirectory:(NSString *)fileDirectory;
/**
Remove route from cache.
*/
- (void)removeRoute:(LLLocationMockRouteModel *)model;
/**
Start mock route locations.
*/
- (void)startMockRoute:(LLLocationMockRouteModel *)model;
/**
Stop current mock route.
*/
- (void)stopMockRoute;
/**
Start record route.
*/
- (BOOL)startRecordRoute;
/**
Stop record route.
*/
- (NSString *_Nullable)stopRecordRoute;
/**
Whether is LLDebugTool's data in file extend.
*/
+ (BOOL)isLLDebugToolLocationRouteFile:(NSString *)path;
/**
Add LLDebugTool extend data to file.
*/
+ (BOOL)addLLDebugToolExtendDataWithPath:(NSString *)path;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,338 @@
//
// LLLocationHelper.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLLocationHelper.h"
#import <pthread/pthread.h>
#import "LLLocationMockRouteModel.h"
#import "LLInternalMacros.h"
#import "LLFormatterTool.h"
#import "LLToastUtils.h"
#import "LLConfig.h"
#import "CLLocationManager+LL_Location.h"
#import "CLLocation+LL_Location.h"
static LLLocationHelper *_instance = nil;
static pthread_mutex_t mutex_t = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t route_mutex_t = PTHREAD_MUTEX_INITIALIZER;
@interface LLLocationHelper () <CLLocationManagerDelegate>
@property (nonatomic, strong) NSHashTable <CLLocationManager *>*managers;
@property (nonatomic, strong) NSMutableArray <LLLocationMockRouteModel *>*routes;
@property (nonatomic, strong) LLLocationMockRouteModel *routeModel;
@property (nonatomic, strong) NSTimer *mockRouteTimer;
@property (nonatomic, strong) CLLocationManager *locationManager;
@property (nonatomic, strong) NSMutableArray <CLLocation *>*locations;
@end
@implementation LLLocationHelper
+ (instancetype)shared {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[LLLocationHelper alloc] init];
});
return _instance;
}
#pragma mark - Public
- (void)addMockRouteFile:(NSString *)filePath {
// Check nil.
if ([filePath length] == 0) {
return;
}
// Check file extension.
if (![filePath.pathExtension isEqualToString:@"json"]) {
return;
}
// Get name.
NSString *name = [filePath.lastPathComponent stringByDeletingPathExtension];
LLLocationMockRouteModel *model = [[LLLocationMockRouteModel alloc] initWithJsonFile:filePath timeInterval:[LLConfig shared].mockRouteTimeInterval name:name];
[self addRoute:model];
}
- (void)addMockRouteDirectory:(NSString *)fileDirectory {
if ([fileDirectory length] == 0) {
return;
}
BOOL isDirectory = NO;
if (![[NSFileManager defaultManager] fileExistsAtPath:fileDirectory isDirectory:&isDirectory]) {
return;
}
if (!isDirectory) {
[self addMockRouteFile:fileDirectory];
return;
}
NSArray *filePaths = [[NSFileManager defaultManager] subpathsAtPath:fileDirectory];
for (NSString *filePath in filePaths) {
[self addMockRouteFile:[fileDirectory stringByAppendingPathComponent:filePath]];
}
}
- (void)removeRoute:(LLLocationMockRouteModel *)model {
pthread_mutex_lock(&route_mutex_t);
[self.routes removeObject:model];
_availableRoutes = [self.routes copy];
pthread_mutex_unlock(&route_mutex_t);
}
- (void)startMockRoute:(LLLocationMockRouteModel *)model {
[self.routeModel reload];
self.routeModel = model;
_isMockRoute = YES;
[self startTimer];
[[LLToastUtils shared] toastMessage:LLLocalizedString(@"location.start.route")];
}
- (void)stopMockRoute {
[self.routeModel reload];
_isMockRoute = NO;
[self stopTimer];
[[LLToastUtils shared] toastMessage:LLLocalizedString(@"location.stop.route")];
}
- (BOOL)startRecordRoute {
if (![CLLocationManager locationServicesEnabled]) {
return NO;
}
[self.locations removeAllObjects];
[self.locationManager startUpdatingLocation];
return YES;
}
- (NSString *)stopRecordRoute {
[self.locationManager stopUpdatingLocation];
if (self.locations.count == 0) {
return nil;
}
NSMutableDictionary *json = [[NSMutableDictionary alloc] init];
json[@"key"] = @"LLDebugTool";
// NSMutableArray *data = [[NSMutableArray alloc] init];
for (CLLocation *location in self.locations) {
NSMutableDictionary *locationJson = [[NSMutableDictionary alloc] init];
locationJson[@"lng"] = [LLFormatterTool formatLocation:@(location.coordinate.longitude)];
locationJson[@"lat"] = [LLFormatterTool formatLocation:@(location.coordinate.latitude)];
}
return nil;
}
+ (BOOL)isLLDebugToolLocationRouteFile:(NSString *)path {
return [[self fileExtendedAttributesWithPath:path] objectForKey:@"LLDebugTool"] ? YES : NO;
}
+ (BOOL)addLLDebugToolExtendDataWithPath:(NSString *)path {
if ([path length] == 0) {
return NO;
}
NSDictionary *extendedAttributes = [self fileExtendedAttributesWithPath:path];
if (extendedAttributes[@"LLDebugTool"]) {
return YES;
}
NSMutableDictionary *newExtendedAttributes = [[NSMutableDictionary alloc] init];
if (extendedAttributes) {
[newExtendedAttributes addEntriesFromDictionary:extendedAttributes];
}
NSData *data = [@"LLDebugTool" dataUsingEncoding:NSUTF8StringEncoding];
[newExtendedAttributes setObject:data forKey:@"LLDebugTool"];
NSError *error = nil;
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error];
if (error) {
return NO;
}
NSMutableDictionary *newAttributes = [[NSMutableDictionary alloc] init];
if (attributes) {
[newAttributes addEntriesFromDictionary:attributes];
}
[newAttributes setObject:newExtendedAttributes forKey:@"NSFileExtendedAttributes"];
if (![[NSFileManager defaultManager] setAttributes:newAttributes ofItemAtPath:path error:&error]) {
return NO;
}
if (error) {
return NO;
}
return YES;
}
#pragma mark - Life cycle
- (instancetype)init {
if (self = [super init]) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveLLCLLocationRegisterNotification:) name:LLCLLocationRegisterNotificationName object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveLLCLLocationUnRegisterNotification:) name:LLCLLocationUnRegisterNotificationName object:nil];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:LLCLLocationRegisterNotificationName object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:LLCLLocationUnRegisterNotificationName object:nil];
}
#pragma mark - NSNotification
- (void)didReceiveLLCLLocationRegisterNotification:(NSNotification *)notification {
[self registerManager:notification.object];
}
- (void)didReceiveLLCLLocationUnRegisterNotification:(NSNotification *)notification {
[self unregisterManager:notification.object];
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
CLLocation *location = [locations firstObject];
[self.locations addObject:location];
}
#pragma mark - Primary
- (void)addRoute:(LLLocationMockRouteModel *)model {
pthread_mutex_lock(&route_mutex_t);
if ([model.name length]) {
if (![self.routes containsObject:model]) {
[self.routes addObject:model];
_availableRoutes = [self.routes copy];
}
}
pthread_mutex_unlock(&route_mutex_t);
}
- (void)registerManager:(CLLocationManager *)manager {
if (!manager || ![manager isKindOfClass:[CLLocationManager class]] || manager == _locationManager) {
return;
}
pthread_mutex_lock(&mutex_t);
[self.managers addObject:manager];
pthread_mutex_unlock(&mutex_t);
}
- (void)unregisterManager:(CLLocationManager *)manager {
if (!manager || ![manager isKindOfClass:[CLLocationManager class]] || manager == _locationManager) {
return;
}
pthread_mutex_lock(&mutex_t);
[self.managers removeObject:manager];
pthread_mutex_unlock(&mutex_t);
}
- (NSArray <CLLocationManager *>*)allManagers {
NSArray *managers = nil;
pthread_mutex_lock(&mutex_t);
managers = [self.managers allObjects];
pthread_mutex_unlock(&mutex_t);
return managers;
}
- (void)startTimer {
[self stopTimer];
self.mockRouteTimer = [NSTimer scheduledTimerWithTimeInterval:self.routeModel.timeInterval target:self selector:@selector(routeTimerAction:) userInfo:nil repeats:YES];
}
- (void)stopTimer {
if ([self.mockRouteTimer isValid]) {
[self.mockRouteTimer invalidate];
self.mockRouteTimer = nil;
}
}
- (void)routeTimerAction:(NSTimer *)timer {
CLLocation *location = [self.routeModel nextLocation];
if (location) {
NSArray *managers = [self allManagers];
[managers enumerateObjectsUsingBlock:^(CLLocationManager *obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj.LL_isUpdatingLocation && [obj.delegate respondsToSelector:@selector(locationManager:didUpdateLocations:)]) {
[obj.delegate locationManager:obj didUpdateLocations:@[location]];
}
}];
} else {
[self stopTimer];
}
}
+ (NSDictionary *)fileExtendedAttributesWithPath:(NSString *)path {
if ([path length] == 0) {
return nil;
}
NSError *error = nil;
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error];
if (error || !attributes) {
return nil;
}
NSDictionary *extendedAttributes = attributes[@"NSFileExtendedAttributes"];
if (!extendedAttributes || ![extendedAttributes isKindOfClass:[NSDictionary class]]) {
return nil;
}
return extendedAttributes;
}
#pragma mark - Getters and setters
- (NSHashTable<CLLocationManager *> *)managers {
if (!_managers) {
_managers = [NSHashTable weakObjectsHashTable];
}
return _managers;
}
- (NSMutableArray<LLLocationMockRouteModel *> *)routes {
if (!_routes) {
_routes = [[NSMutableArray alloc] init];
}
return _routes;
}
- (CLLocationManager *)locationManager {
if (!_locationManager) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
}
return _locationManager;
}
- (NSMutableArray<CLLocation *> *)locations {
if (!_locations) {
_locations = [[NSMutableArray alloc] init];
}
return _locations;
}
@end

View File

@@ -0,0 +1,71 @@
//
// LLLocationMockRouteModel.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLBaseModel.h"
#import <CoreLocation/CoreLocation.h>
NS_ASSUME_NONNULL_BEGIN
/// Mock Route model.
@interface LLLocationMockRouteModel : LLBaseModel
/// Mock locations
@property (nonatomic, copy, nullable) NSArray <CLLocation *>*locations;
/// Time interval between two location.
@property (nonatomic, assign) NSTimeInterval timeInterval;
/// Whether model is available.
@property (nonatomic, assign, readonly) BOOL isAvailable;
/// Model name.
@property (nonatomic, copy, readonly) NSString *name;
/// Model file path.
@property (nonatomic, copy, readonly) NSString *filePath;
/// Initial method.
/// @param locations Mock locations.
/// @param timeInterval Time interval between two location.
/// @param name Model name.
- (instancetype)initWithLocation:(NSArray <CLLocation *>*)locations timeInterval:(NSTimeInterval)timeInterval name:(NSString *)name;
/// Initial a model by a file.
/// @param filePath File path.
/// @param timeInterval Time interval between two location.
/// @param name Model name.
- (instancetype)initWithJsonFile:(NSString *)filePath timeInterval:(NSTimeInterval)timeInterval name:(NSString *)name;
/// Next location.
- (CLLocation *_Nullable)nextLocation;
/// Is finish all location.
- (BOOL)isFinish;
/// Reload to start.
- (void)reload;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,146 @@
//
// LLLocationMockRouteModel.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLLocationMockRouteModel.h"
#import "CLLocation+LL_Location.h"
@interface LLLocationMockRouteModel ()
@property (nonatomic, copy) NSString *filePath;
@property (nonatomic, assign) NSInteger index;
@property (nonatomic, assign) BOOL checkAvailable;
@property (nonatomic, assign) BOOL isAvailable;
@end
@implementation LLLocationMockRouteModel
- (instancetype)initWithLocation:(NSArray <CLLocation *>*)locations timeInterval:(NSTimeInterval)timeInterval name:(NSString *)name {
if (self = [super init]) {
_locations = [locations copy];
_timeInterval = timeInterval;
_name = [name copy];
_checkAvailable = YES;
_isAvailable = YES;
}
return self;
}
- (instancetype)initWithJsonFile:(NSString *)filePath timeInterval:(NSTimeInterval)timeInterval name:(NSString *)name {
if (self = [super init]) {
_filePath = filePath;
_timeInterval = timeInterval;
_name = [name copy];
}
return self;
}
- (CLLocation *)nextLocation {
if (self.index < self.locations.count) {
CLLocation *location = self.locations[self.index];
self.index++;
return location;
}
return nil;
}
- (BOOL)isFinish {
if (self.index >= self.locations.count) {
return YES;
}
return NO;
}
- (void)reload {
self.index = 0;
}
- (BOOL)isAvailable {
if (!_checkAvailable) {
[self analysisJsonFile:self.filePath];
_checkAvailable = YES;
}
return _isAvailable;
}
#pragma mark - Primary
- (void)analysisJsonFile:(NSString *)filePath {
// Check nil.
if ([filePath length] == 0) {
return;
}
// Check file extension.
if (![filePath.pathExtension isEqualToString:@"json"]) {
return;
}
// Convert to data.
NSData *data = [[NSData alloc] initWithContentsOfFile:filePath];
if ([data length] == 0) {
return;
}
id object = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
// Check type.
if (![object isKindOfClass:[NSDictionary class]]) {
return;
}
NSDictionary *json = (NSDictionary *)object;
// Check key.
if (![json[@"key"] isEqualToString:@"LLDebugTool"]) {
return;
}
NSArray *jsonData = json[@"data"];
// Check data.
if (![jsonData isKindOfClass:[NSArray class]]) {
return;
}
// Add data.
NSMutableArray *locations = [[NSMutableArray alloc] init];
for (id obj in jsonData) {
if ([obj isKindOfClass:[NSDictionary class]]) {
NSDictionary *dic = (NSDictionary *)obj;
if (dic[@"lat"] && dic[@"lng"]) {
CLLocationDegrees lat = [dic[@"lat"] doubleValue];
CLLocationDegrees lng = [dic[@"lng"] doubleValue];
CLLocation *location = [[CLLocation alloc] initWithLatitude:lat longitude:lng];
location.LL_mock = YES;
[locations addObject:location];
}
}
}
_locations = [locations copy];
_isAvailable = YES;
}
@end

View File

@@ -0,0 +1,34 @@
//
// LLLocationProxy.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLProxy.h"
#import <CoreLocation/CoreLocation.h>
NS_ASSUME_NONNULL_BEGIN
@interface LLLocationProxy : LLProxy <CLLocationManagerDelegate>
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,61 @@
//
// LLLocationProxy.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLLocationProxy.h"
#import "LLLocationHelper.h"
#import "LLConfig.h"
#import "CLLocation+LL_Location.h"
@implementation LLLocationProxy
- (BOOL)respondsToSelector:(SEL)aSelector {
if (aSelector == @selector(locationManager:didUpdateLocations:)) {
return YES;
}
return [super respondsToSelector:aSelector];
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
if ([self.target respondsToSelector:_cmd]) {
if ([LLLocationHelper shared].isMockRoute) {
CLLocation *location = [locations firstObject];
// Mocking route.
if (location && !location.LL_isMock) {
// Real location, ignore.
return;
}
} else if ([LLLocationHelper shared].enable) {
// Mock location.
CLLocation *mockLocation = [[CLLocation alloc] initWithLatitude:[LLConfig shared].mockLocationLatitude longitude:[LLConfig shared].mockLocationLongitude];
mockLocation.LL_mock = YES;
locations = @[mockLocation];
}
[self.target locationManager:manager didUpdateLocations:locations];
}
}
@end

View File

@@ -0,0 +1,32 @@
//
// MKMapView+LL_Location.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <MapKit/MapKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface MKMapView (LL_Location)
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,47 @@
//
// MKMapView+LL_Location.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "MKMapView+LL_Location.h"
#import "LLLocationHelper.h"
#import "LLConfig.h"
#import "NSObject+LL_Runtime.h"
@implementation MKMapView (LL_Location)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
[self LL_swizzleInstanceMethodWithOriginSel:NSSelectorFromString(@"_updateUserLocationViewWithLocation:hadUserLocation:") swizzledSel:@selector(LL_updateUserLocationViewWithLocation:hadUserLocation:)];
});
}
- (void)LL_updateUserLocationViewWithLocation:(CLLocation *)location hadUserLocation:(BOOL)hadUserLocation {
if ([LLLocationHelper shared].enable) {
location = [[CLLocation alloc] initWithLatitude:[LLConfig shared].mockLocationLatitude longitude:[LLConfig shared].mockLocationLongitude];
}
[self LL_updateUserLocationViewWithLocation:location hadUserLocation:hadUserLocation];
}
@end

View File

@@ -0,0 +1,44 @@
//
// LLLocation.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef LLLocation_h
#define LLLocation_h
#import "LLLocationComponent.h"
#import "LLLocationMockRouteModel.h"
#import "LLLocationHelper.h"
#import "LLLocationProxy.h"
#import "CLLocationManager+LL_Location.h"
#import "CLLocation+LL_Location.h"
#import "MKMapView+LL_Location.h"
#import "LLLocationViewController.h"
#import "LLPinAnnotationView.h"
#import "LLLocationWindow.h"
#import "LLAnnotation.h"
#import "LLWindowManager+Location.h"
#endif /* LLLocation_h */

View File

@@ -0,0 +1,32 @@
//
// LLLocationComponent.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLComponent.h"
NS_ASSUME_NONNULL_BEGIN
@interface LLLocationComponent : LLComponent
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,43 @@
//
// LLLocationComponent.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLLocationComponent.h"
#import "LLLocationViewController.h"
#import "LLNavigationController.h"
#import "LLWindowManager+Location.h"
@implementation LLLocationComponent
- (void)componentDidLoad:(NSDictionary<NSString *,id> *)data {
LLBaseWindow *window = [[LLWindowManager shared] visiableWindow];
if ([window isKindOfClass:[LLFunctionWindow class]]) {
LLNavigationController *nav = (LLNavigationController *)window.rootViewController;
[nav pushViewController:[[LLLocationViewController alloc] init] animated:YES];
} else {
[[LLWindowManager shared] showWindow:[LLWindowManager locationWindow] animated:YES];
}
}
@end

View File

@@ -0,0 +1,41 @@
//
// LLAnnotation.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <MapKit/MapKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface LLAnnotation : NSObject <MKAnnotation>
/// coordinate
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
/// Title
@property (nonatomic, readonly, copy, nullable) NSString *title;
/// Subtitle
@property (nonatomic, readonly, copy, nullable) NSString *subtitle;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,33 @@
//
// LLAnnotation.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLAnnotation.h"
@implementation LLAnnotation
- (void)setCoordinate:(CLLocationCoordinate2D)coordinate {
_coordinate = coordinate;
_title = [NSString stringWithFormat:@"%0.6f, %0.6f", coordinate.latitude, coordinate.longitude];
}
@end

View File

@@ -0,0 +1,32 @@
//
// LLLocationViewController.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLBaseComponentViewController.h"
NS_ASSUME_NONNULL_BEGIN
@interface LLLocationViewController : LLBaseComponentViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,547 @@
//
// LLLocationViewController.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLLocationViewController.h"
#import <MapKit/MapKit.h>
#import "LLDetailTitleSelectorCellView.h"
#import "LLLocationMockRouteModel.h"
#import "LLTitleSwitchCellView.h"
#import "LLPinAnnotationView.h"
#import "LLInternalMacros.h"
#import "LLLocationHelper.h"
#import "LLSettingManager.h"
#import "LLThemeManager.h"
#import "LLAnnotation.h"
#import "LLToastUtils.h"
#import "LLConfig.h"
#import "LLConst.h"
#import "UIViewController+LL_Utils.h"
#import "UIView+LL_Utils.h"
static NSString *const kAnnotationID = @"AnnotationID";
@interface LLLocationViewController () <MKMapViewDelegate, CLLocationManagerDelegate>
@property (nonatomic, strong) LLTitleSwitchCellView *mockLocationSwitch;
@property (nonatomic, strong) LLDetailTitleSelectorCellView *locationDescriptView;
@property (nonatomic, strong) LLDetailTitleSelectorCellView *addressDescriptView;
@property (nonatomic, strong) LLTitleSwitchCellView *mockRouteSwitch;
@property (nonatomic, strong) LLDetailTitleSelectorCellView *routeDescriptView;
@property (nonatomic, strong) LLTitleSwitchCellView *recordRouteSwitch;
@property (nonatomic, strong) NSTimer *recordRouteTimer;
@property (nonatomic, strong) MKMapView *mapView;
@property (nonatomic, strong) LLAnnotation *annotation;
@property (nonatomic, strong) CLLocationManager *locationManager;
@property (nonatomic, strong) CLGeocoder *geocoder;
@property (nonatomic, assign) BOOL isAddAnnotation;
@property (nonatomic, assign) BOOL automicSetRegion;
@property (nonatomic, strong) LLLocationMockRouteModel *routeModel;
@end
@implementation LLLocationViewController
#pragma mark - Life cycle
- (void)viewDidLoad {
[super viewDidLoad];
self.title = LLLocalizedString(@"function.location");
self.view.backgroundColor = [LLThemeManager shared].backgroundColor;
[self.view addSubview:self.mockLocationSwitch];
[self.view addSubview:self.locationDescriptView];
[self.view addSubview:self.addressDescriptView];
[self.view addSubview:self.mockRouteSwitch];
[self.view addSubview:self.routeDescriptView];
[self.view addSubview:self.recordRouteSwitch];
[self.view addSubview:self.mapView];
[self addMockLocationSwitchConstraints];
[self addLocationDescriptViewConstraints];
[self addAddressDescriptViewConstraints];
[self addMockRouteSwitchConstraints];
[self addRouteDescriptViewConstraints];
[self addRecordRouteSwitchConstraints];
[self loadData];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self stopRecordRouteTimer];
}
#pragma mark - Over write
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.mapView.frame = CGRectMake(0, self.addressDescriptView.LL_bottom + kLLGeneralMargin, LL_SCREEN_WIDTH, LL_SCREEN_HEIGHT - self.addressDescriptView.LL_bottom - kLLGeneralMargin);
}
#pragma mark - MKMapViewDelegate
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view didChangeDragState:(MKAnnotationViewDragState)newState fromOldState:(MKAnnotationViewDragState)oldState {
if (oldState == MKAnnotationViewDragStateEnding && newState == MKAnnotationViewDragStateNone) {
id <MKAnnotation> annotation = view.annotation;
if (![annotation isKindOfClass:[LLAnnotation class]]) {
return;
}
if (CLLocationCoordinate2DIsValid(annotation.coordinate)) {
[self setUpCoordinate:annotation.coordinate automicSetRegion:YES placemark:nil];
}
}
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation {
if ([annotation isKindOfClass:[LLAnnotation class]]) {
LLPinAnnotationView *annotationView = (LLPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:kAnnotationID];
if (!annotationView) {
annotationView = [[LLPinAnnotationView alloc] initWithAnnotation:nil reuseIdentifier:kAnnotationID];
}
annotationView.annotation = annotation;
return annotationView;
}
return nil;
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
CLLocation *location = [locations firstObject];
if (location) {
[manager stopUpdatingLocation];
[self setUpCoordinate:location.coordinate automicSetRegion:YES placemark:nil];
}
}
#pragma mark - Primary
- (void)addMockLocationSwitchConstraints {
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:self.mockLocationSwitch attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.mockLocationSwitch.superview attribute:NSLayoutAttributeTop multiplier:1 constant:LL_NAVIGATION_HEIGHT];
NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:self.mockLocationSwitch attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.mockLocationSwitch.superview attribute:NSLayoutAttributeLeading multiplier:1 constant:0];
NSLayoutConstraint *right = [NSLayoutConstraint constraintWithItem:self.mockLocationSwitch attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.mockLocationSwitch.superview attribute:NSLayoutAttributeTrailing multiplier:1 constant:0];
self.mockLocationSwitch.translatesAutoresizingMaskIntoConstraints = NO;
[self.mockLocationSwitch.superview addConstraints:@[top, left, right]];
}
- (void)addLocationDescriptViewConstraints {
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:self.locationDescriptView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.mockLocationSwitch attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:self.locationDescriptView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.locationDescriptView.superview attribute:NSLayoutAttributeLeading multiplier:1 constant:0];
NSLayoutConstraint *right = [NSLayoutConstraint constraintWithItem:self.locationDescriptView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.locationDescriptView.superview attribute:NSLayoutAttributeTrailing multiplier:1 constant:0];
self.locationDescriptView.translatesAutoresizingMaskIntoConstraints = NO;
[self.locationDescriptView.superview addConstraints:@[top, left, right]];
}
- (void)addAddressDescriptViewConstraints {
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:self.addressDescriptView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.locationDescriptView attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:self.addressDescriptView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.addressDescriptView.superview attribute:NSLayoutAttributeLeading multiplier:1 constant:0];
NSLayoutConstraint *right = [NSLayoutConstraint constraintWithItem:self.addressDescriptView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.addressDescriptView.superview attribute:NSLayoutAttributeTrailing multiplier:1 constant:0];
self.addressDescriptView.translatesAutoresizingMaskIntoConstraints = NO;
[self.addressDescriptView.superview addConstraints:@[top, left, right]];
}
- (void)addMockRouteSwitchConstraints {
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:self.mockRouteSwitch attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.addressDescriptView attribute:NSLayoutAttributeBottom multiplier:1 constant:kLLGeneralMargin];
NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:self.mockRouteSwitch attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.mockRouteSwitch.superview attribute:NSLayoutAttributeLeading multiplier:1 constant:0];
NSLayoutConstraint *right = [NSLayoutConstraint constraintWithItem:self.mockRouteSwitch attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.mockRouteSwitch.superview attribute:NSLayoutAttributeTrailing multiplier:1 constant:0];
self.mockRouteSwitch.translatesAutoresizingMaskIntoConstraints = NO;
[self.mockRouteSwitch.superview addConstraints:@[top, left, right]];
}
- (void)addRouteDescriptViewConstraints {
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:self.routeDescriptView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.self.mockRouteSwitch attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:self.routeDescriptView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.routeDescriptView.superview attribute:NSLayoutAttributeLeading multiplier:1 constant:0];
NSLayoutConstraint *right = [NSLayoutConstraint constraintWithItem:self.routeDescriptView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.routeDescriptView.superview attribute:NSLayoutAttributeTrailing multiplier:1 constant:0];
self.routeDescriptView.translatesAutoresizingMaskIntoConstraints = NO;
[self.routeDescriptView.superview addConstraints:@[top, left, right]];
}
- (void)addRecordRouteSwitchConstraints {
NSLayoutConstraint *top = [NSLayoutConstraint constraintWithItem:self.recordRouteSwitch attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.self.routeDescriptView attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:self.recordRouteSwitch attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.recordRouteSwitch.superview attribute:NSLayoutAttributeLeading multiplier:1 constant:0];
NSLayoutConstraint *right = [NSLayoutConstraint constraintWithItem:self.recordRouteSwitch attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.recordRouteSwitch.superview attribute:NSLayoutAttributeTrailing multiplier:1 constant:0];
self.recordRouteSwitch.translatesAutoresizingMaskIntoConstraints = NO;
[self.recordRouteSwitch.superview addConstraints:@[top, left, right]];
}
- (void)setUpCoordinate:(CLLocationCoordinate2D)coordinate automicSetRegion:(BOOL)automicSetRegion placemark:(CLPlacemark *)placemark {
// Set annotation.
self.annotation.coordinate = coordinate;
if ([LLLocationHelper shared].enable) {
[self setUpMockCoordinate:coordinate];
}
// Automic set map region
if (automicSetRegion) {
if (self.automicSetRegion) {
[self.mapView setCenterCoordinate:coordinate animated:YES];
} else {
[self animatedUpdateMapRegion:coordinate];
}
}
// Set location title
[self updateLocationDescriptViewDetailTitle:coordinate];
if (placemark) {
[self updateAddressDescriptViewDetailTitle:placemark];
} else {
[self reverseGeocode:coordinate];
}
// Update
if (!self.isAddAnnotation) {
self.isAddAnnotation = YES;
[self.mapView addAnnotation:self.annotation];
[self.mapView selectAnnotation:self.annotation animated:YES];
} else {
[self.mapView deselectAnnotation:self.annotation animated:NO];
[self.mapView selectAnnotation:self.annotation animated:NO];
}
}
- (void)animatedUpdateMapRegion:(CLLocationCoordinate2D)coordinate {
self.automicSetRegion = YES;
[self.mapView setRegion:MKCoordinateRegionMake(coordinate, MKCoordinateSpanMake(0.05, 0.05)) animated:YES];
}
- (void)updateLocationDescriptViewDetailTitle:(CLLocationCoordinate2D)coordinate {
self.locationDescriptView.detailTitle = [NSString stringWithFormat:@"%0.6f, %0.6f", coordinate.latitude, coordinate.longitude];
}
- (void)updateAddressDescriptViewDetailTitle:(CLPlacemark *)placemark {
NSString *name = placemark.name;
NSString *locality = placemark.locality;
NSString *administrativeArea = placemark.administrativeArea;
NSString *description = @"";
if (name) {
description = [description stringByAppendingString:name];
}
if (locality && ![description hasPrefix:locality]) {
if (!administrativeArea || ![description hasPrefix:[NSString stringWithFormat:@"%@%@",administrativeArea, locality]]) {
description = [locality stringByAppendingString:description];
}
}
if (administrativeArea && ![description hasPrefix:administrativeArea]) {
description = [administrativeArea stringByAppendingString:description];
}
self.addressDescriptView.detailTitle = description;
}
- (void)updateMockLocationSwitchValue:(BOOL)isOn {
[LLLocationHelper shared].enable = isOn;
[LLSettingManager shared].mockLocationEnable = @(isOn);
if (isOn) {
[self setUpMockCoordinate:self.annotation.coordinate];
}
}
- (void)setUpMockCoordinate:(CLLocationCoordinate2D)coordinate {
[LLConfig shared].mockLocationLatitude = coordinate.latitude;
[LLConfig shared].mockLocationLongitude = coordinate.longitude;
[LLSettingManager shared].mockLocationLatitude = @(coordinate.latitude);
[LLSettingManager shared].mockLocationLongitude = @(coordinate.longitude);
}
- (void)reverseGeocode:(CLLocationCoordinate2D)coordinate {
[self.geocoder cancelGeocode];
__weak typeof(self) weakSelf = self;
[self.geocoder reverseGeocodeLocation:[[CLLocation alloc] initWithLatitude:coordinate.latitude longitude:coordinate.longitude] completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
if (!error && placemarks.count > 0) {
CLPlacemark *placemark = placemarks.firstObject;
[weakSelf updateAddressDescriptViewDetailTitle:placemark];
}
}];
}
- (void)geocodeAddress:(NSString *)address {
[self.geocoder cancelGeocode];
__weak typeof(self) weakSelf = self;
[self.geocoder geocodeAddressString:address inRegion:nil completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
if (!error && placemarks.count > 0) {
CLPlacemark *placemark = placemarks.firstObject;
[weakSelf setUpCoordinate:placemark.location.coordinate automicSetRegion:YES placemark:placemark];
}
}];
}
- (void)updateMockRouteSwitchValue:(BOOL)isOn {
if (isOn) {
if (self.routeModel) {
[[LLLocationHelper shared] startMockRoute:self.routeModel];
} else {
[[LLToastUtils shared] toastMessage:LLLocalizedString(@"location.select.route")];
self.mockRouteSwitch.on = NO;
}
} else {
[[LLLocationHelper shared] stopMockRoute];
}
}
- (void)selectMockRoute:(LLLocationMockRouteModel *)model {
if (model.isAvailable) {
self.routeModel = model;
[[LLLocationHelper shared] startMockRoute:model];
self.mockRouteSwitch.on = YES;
[LLSettingManager shared].mockRouteFilePath = model.filePath;
[LLSettingManager shared].mockRouteFileName = model.name;
} else {
[[LLToastUtils shared] toastMessage:LLLocalizedString(@"location.route.file.error")];
}
}
- (void)updateRecordRouteSwitchValue:(BOOL)isOn {
if (isOn) {
if ([LLLocationHelper shared].isMockRoute || [LLLocationHelper shared].enable) {
__weak typeof(self) weakSelf = self;
[self LL_showAlertControllerWithMessage:LLLocalizedString(@"location.record.route.alert") handler:^(NSInteger action) {
if (action == 1) {
[weakSelf startRecordRoute];
} else {
[weakSelf stopRecordRoute];
}
}];
} else {
[self startRecordRoute];
}
} else {
[self stopRecordRoute];
}
}
- (void)startRecordRoute {
self.recordRouteSwitch.on = YES;
self.recordRouteSwitch.detailTitle = LLLocalizedString(@"location.record.route.recording");
[self startRecordRouteTimer];
}
- (void)stopRecordRoute {
self.recordRouteSwitch.on = NO;
self.recordRouteSwitch.detailTitle = nil;
[self stopRecordRouteTimer];
}
- (void)startRecordRouteTimer {
[self stopRecordRouteTimer];
self.recordRouteTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(recordRouteTimerAction:) userInfo:nil repeats:YES];
}
- (void)stopRecordRouteTimer {
if ([self.recordRouteTimer isValid]) {
[self.recordRouteTimer invalidate];
self.recordRouteTimer = nil;
}
}
- (void)recordRouteTimerAction:(NSTimer *)timer {
if ([self.recordRouteSwitch.detailTitle hasSuffix:@"..."]) {
self.recordRouteSwitch.detailTitle = [self.recordRouteSwitch.detailTitle substringToIndex:self.recordRouteSwitch.detailTitle.length - 3];
} else {
self.recordRouteSwitch.detailTitle = [self.recordRouteSwitch.detailTitle stringByAppendingString:@"."];
}
}
- (void)loadData {
CLLocationCoordinate2D mockCoordinate = CLLocationCoordinate2DMake([LLConfig shared].mockLocationLatitude, [LLConfig shared].mockLocationLongitude);
BOOL automicSetRegion = YES;
if (mockCoordinate.latitude == 0 && mockCoordinate.longitude == 0) {
mockCoordinate = CLLocationCoordinate2DMake(kLLDefaultMockLocationLatitude, kLLDefaultMockLocationLongitude);
automicSetRegion = NO;
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedAlways || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse) {
[self.locationManager startUpdatingLocation];
}
}
[self setUpCoordinate:mockCoordinate automicSetRegion:automicSetRegion placemark:nil];
}
#pragma mark - Event response
- (void)locationDescriptViewDidSelect {
__weak typeof(self) weakSelf = self;
[self LL_showTextFieldAlertControllerWithMessage:LLLocalizedString(@"location.lat.lng") text:self.locationDescriptView.detailTitle handler:^(NSString * _Nullable newText) {
NSString *text = [newText stringByReplacingOccurrencesOfString:@" " withString:@""];
NSArray *array = [text componentsSeparatedByString:@","];
if (array.count != 2) {
return;
}
CLLocationDegrees lat = [array[0] doubleValue];
CLLocationDegrees lng = [array[1] doubleValue];
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(lat, lng);
if (CLLocationCoordinate2DIsValid(coordinate)) {
[weakSelf setUpCoordinate:coordinate automicSetRegion:YES placemark:nil];
}
}];
}
- (void)addressDescriptViewDidSelect {
__weak typeof(self) weakSelf = self;
[self LL_showTextFieldAlertControllerWithMessage:LLLocalizedString(@"location.address") text:self.addressDescriptView.detailTitle handler:^(NSString * _Nullable newText) {
[weakSelf geocodeAddress:newText];
}];
}
- (void)routeDescriptViewDidSelect {
__weak typeof(self) weakSelf = self;
NSMutableArray *actions = [[NSMutableArray alloc] init];
__block NSArray *models = [[LLLocationHelper shared].availableRoutes copy];
for (LLLocationMockRouteModel *model in models) {
[actions addObject:model.name];
}
[self LL_showActionSheetWithTitle:LLLocalizedString(@"location.select.route") actions:actions currentAction:nil completion:^(NSInteger index) {
[weakSelf selectMockRoute:models[index]];
}];
}
#pragma mark - Getters and setters
- (LLTitleSwitchCellView *)mockLocationSwitch {
if (!_mockLocationSwitch) {
_mockLocationSwitch = [[LLTitleSwitchCellView alloc] init];
_mockLocationSwitch.backgroundColor = [LLThemeManager shared].containerColor;
_mockLocationSwitch.title = LLLocalizedString(@"location.mock.location");
_mockLocationSwitch.on = [LLLocationHelper shared].enable;
__weak typeof(self) weakSelf = self;
_mockLocationSwitch.changePropertyBlock = ^(BOOL isOn) {
[weakSelf updateMockLocationSwitchValue:isOn];
};
[_mockLocationSwitch needLine];
}
return _mockLocationSwitch;
}
- (LLDetailTitleSelectorCellView *)locationDescriptView {
if (!_locationDescriptView) {
_locationDescriptView = [[LLDetailTitleSelectorCellView alloc] init];
_locationDescriptView.backgroundColor = [LLThemeManager shared].containerColor;
_locationDescriptView.title = LLLocalizedString(@"location.lat.lng");
_locationDescriptView.detailTitle = @"0, 0";
[_locationDescriptView needLine];
__weak typeof(self) weakSelf = self;
_locationDescriptView.block = ^{
[weakSelf locationDescriptViewDidSelect];
};
}
return _locationDescriptView;
}
- (LLDetailTitleSelectorCellView *)addressDescriptView {
if (!_addressDescriptView) {
_addressDescriptView = [[LLDetailTitleSelectorCellView alloc] init];
_addressDescriptView.backgroundColor = [LLThemeManager shared].containerColor;
_addressDescriptView.title = LLLocalizedString(@"location.address");
[_addressDescriptView needFullLine];
__weak typeof(self) weakSelf = self;
_addressDescriptView.block = ^{
[weakSelf addressDescriptViewDidSelect];
};
}
return _addressDescriptView;
}
- (LLTitleSwitchCellView *)mockRouteSwitch {
if (!_mockRouteSwitch) {
_mockRouteSwitch = [[LLTitleSwitchCellView alloc] init];
_mockRouteSwitch.backgroundColor = [LLThemeManager shared].containerColor;
_mockRouteSwitch.title = LLLocalizedString(@"location.mock.route");
_mockRouteSwitch.on = [LLLocationHelper shared].isMockRoute;
__weak typeof(self) weakSelf = self;
_mockRouteSwitch.changePropertyBlock = ^(BOOL isOn) {
[weakSelf updateMockRouteSwitchValue:isOn];
};
[_mockRouteSwitch needLine];
}
return _mockRouteSwitch;
}
- (LLDetailTitleSelectorCellView *)routeDescriptView {
if (!_routeDescriptView) {
_routeDescriptView = [[LLDetailTitleSelectorCellView alloc] init];
_routeDescriptView.backgroundColor = [LLThemeManager shared].containerColor;
_routeDescriptView.title = LLLocalizedString(@"location.route");
_routeDescriptView.detailTitle = [LLSettingManager shared].mockRouteFileName ? : LLLocalizedString(@"location.select.route");
[_routeDescriptView needLine];
__weak typeof(self) weakSelf = self;
_routeDescriptView.block = ^{
[weakSelf routeDescriptViewDidSelect];
};
}
return _routeDescriptView;
}
- (LLTitleSwitchCellView *)recordRouteSwitch {
if (!_recordRouteSwitch) {
_recordRouteSwitch = [[LLTitleSwitchCellView alloc] init];
_recordRouteSwitch.backgroundColor = [LLThemeManager shared].containerColor;
_recordRouteSwitch.title = LLLocalizedString(@"location.record.route");
__weak typeof(self) weakSelf = self;
_recordRouteSwitch.changePropertyBlock = ^(BOOL isOn) {
[weakSelf updateRecordRouteSwitchValue:isOn];
};
[_recordRouteSwitch needFullLine];
}
return _recordRouteSwitch;
}
- (void)setRouteModel:(LLLocationMockRouteModel *)routeModel {
if (_routeModel != routeModel) {
_routeModel = routeModel;
self.routeDescriptView.detailTitle = routeModel.name ?: LLLocalizedString(@"unknown");
}
}
- (MKMapView *)mapView {
if (!_mapView) {
_mapView = [[MKMapView alloc] initWithFrame:CGRectZero];
_mapView.delegate = self;
}
return _mapView;
}
- (LLAnnotation *)annotation {
if (!_annotation) {
_annotation = [[LLAnnotation alloc] init];
}
return _annotation;
}
- (CLLocationManager *)locationManager {
if (!_locationManager) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
}
return _locationManager;
}
- (CLGeocoder *)geocoder {
if (!_geocoder) {
_geocoder = [[CLGeocoder alloc] init];
}
return _geocoder;
}
@end

View File

@@ -0,0 +1,32 @@
//
// LLLocationWindow.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLComponentWindow.h"
NS_ASSUME_NONNULL_BEGIN
@interface LLLocationWindow : LLComponentWindow
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,48 @@
//
// LLLocationWindow.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLLocationWindow.h"
#import "LLLocationViewController.h"
#import "LLNavigationController.h"
#import "LLWindowManager.h"
@implementation LLLocationWindow
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.showAnimateStyle = LLBaseWindowShowAnimateStylePresent;
self.hideAnimateStyle = LLBaseWindowHideAnimateStyleDismiss;
if (!self.rootViewController) {
self.rootViewController = [[LLNavigationController alloc] initWithRootViewController:[[LLLocationViewController alloc] init]];
}
}
return self;
}
#pragma mark - Over write
- (void)componentDidFinish {
[[LLWindowManager shared] showEntryWindow];
}
@end

View File

@@ -0,0 +1,32 @@
//
// LLPinAnnotationView.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <MapKit/MapKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface LLPinAnnotationView : MKPinAnnotationView
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,37 @@
//
// LLPinAnnotationView.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLPinAnnotationView.h"
@implementation LLPinAnnotationView
- (instancetype)initWithAnnotation:(id<MKAnnotation>)annotation reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier]) {
self.draggable = YES;
self.animatesDrop = YES;
self.canShowCallout = YES;
}
return self;
}
@end

View File

@@ -0,0 +1,37 @@
//
// LLWindowManager+Location.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLWindowManager.h"
#import "LLLocationWindow.h"
NS_ASSUME_NONNULL_BEGIN
@interface LLWindowManager (Location)
/// Location window.
+ (LLLocationWindow *)locationWindow;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,32 @@
//
// LLWindowManager+Location.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLWindowManager+Location.h"
@implementation LLWindowManager (Location)
+ (LLLocationWindow *)locationWindow {
return (LLLocationWindow *)[self createWindowWithClassName:NSStringFromClass([LLLocationWindow class])];
}
@end

View File

@@ -0,0 +1,66 @@
//
// LLLogHelper.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import <Foundation/Foundation.h>
#import "LLConfig.h"
NS_ASSUME_NONNULL_BEGIN
/**
Quick print and save log.
*/
@interface LLLogHelper : NSObject
/**
Singleton to control enable.
@return Singleton
*/
+ (instancetype)shared;
/**
Set enable to save log model.
*/
@property (nonatomic, assign, getter=isEnabled) BOOL enable;
/**
Return log levels string.
*/
+ (NSArray <NSString *>*)levelsDescription;
/**
Print and save a log model with infos.
@param file File name.
@param function Function name.
@param lineNo Line number.
@param level Log level.
@param onEvent Event,can filter by this.
@param message Message.
*/
- (void)logInFile:(NSString *_Nullable)file function:(NSString *_Nullable)function lineNo:(NSInteger)lineNo level:(LLConfigLogLevel)level onEvent:(NSString *_Nullable)onEvent message:(NSString *_Nullable)message;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,101 @@
//
// LLLogHelper.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLLogHelper.h"
#import "LLStorageManager.h"
#import "LLFormatterTool.h"
#import "LLLogModel.h"
#import "LLConfig.h"
#import "NSObject+LL_Utils.h"
static LLLogHelper *_instance = nil;
@implementation LLLogHelper
+ (instancetype)shared {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[LLLogHelper alloc] init];
});
return _instance;
}
+ (NSArray <NSString *>*)levelsDescription {
return @[@"Default",@"Alert",@"Warning",@"Error"];
}
- (void)logInFile:(NSString *)file function:(NSString *)function lineNo:(NSInteger)lineNo level:(LLConfigLogLevel)level onEvent:(NSString *)onEvent message:(NSString *)message {
NSString *date = [LLFormatterTool stringFromDate:[NSDate date] style:FormatterToolDateStyle1];
LLConfigLogStyle logStyle = [LLConfig shared].logStyle;
switch (logStyle) {
case LLConfigLogDetail:
case LLConfigLogFileFuncDesc:
case LLConfigLogFileDesc:{
NSString *header = @"\n--------Debug Tool--------";
NSString *onEventString = [NSString stringWithFormat:@"\nEvent:<%@>",onEvent];
NSString *fileString = [NSString stringWithFormat:@"\nFile:<%@>",file];
NSString *lineNoString = [NSString stringWithFormat:@"\nLine:<%ld>",(long)lineNo];
NSString *funcString = [NSString stringWithFormat:@"\nFunc:<%@>",function];
NSString *dateString = [NSString stringWithFormat:@"\nDate:<%@>",date];
NSString *messageString = [NSString stringWithFormat:@"\nDesc:<%@>",message];
NSString *footer = @"\n--------------------------";
NSMutableString *log = [[NSMutableString alloc] initWithString:header];
if (onEvent.length) {
[log appendString:onEventString];
}
[log appendString:fileString];
if (logStyle == LLConfigLogDetail) {
[log appendString:lineNoString];
}
if (logStyle == LLConfigLogDetail || logStyle == LLConfigLogFileFuncDesc) {
[log appendString:funcString];
}
if (logStyle == LLConfigLogDetail) {
[log appendString:dateString];
}
[log appendString:messageString];
[log appendString:footer];
NSLog(@"%@", log);
}
break;
case LLConfigLogNone: {
}
break;
case LLConfigLogNormal:
default:{
NSLog(@"%@",message);
}
break;
}
if (_enable) {
LLLogModel *model = [[LLLogModel alloc] initWithFile:file lineNo:lineNo function:function level:level onEvent:onEvent message:message date:date launchDate:[NSObject LL_launchDate] userIdentity:[LLConfig shared].userIdentity];
[[LLStorageManager shared] saveModel:model complete:nil];
}
}
@end

View File

@@ -0,0 +1,103 @@
//
// LLLogModel.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLTableViewSelectableModel.h"
#import "LLLogHelper.h"
NS_ASSUME_NONNULL_BEGIN
/**
Log model. Save and show log infos.
*/
@interface LLLogModel : LLTableViewSelectableModel
/**
File name.
*/
@property (nonatomic, copy, readonly, nullable) NSString *file;
/**
Line number.
*/
@property (nonatomic, assign, readonly) NSInteger lineNo;
/**
Function name.
*/
@property (nonatomic, copy, readonly, nullable) NSString *function;
/**
Log level.
*/
@property (nonatomic, assign, readonly) LLConfigLogLevel level;
/**
Event.
*/
@property (nonatomic, copy, readonly, nullable) NSString *event;
/**
Message.
*/
@property (nonatomic, copy, readonly) NSString *message;
/**
Print log's date.
*/
@property (nonatomic, copy, readonly) NSString *date;
/**
App launch date.
*/
@property (nonatomic, copy, readonly) NSString *launchDate;
/**
User identity in LLConfig when printing.
*/
@property (nonatomic, copy, readonly, nullable) NSString *userIdentity;
/**
Model identity.
*/
@property (nonatomic, copy, readonly) NSString *identity;
#pragma mark - Quick Getter
/**
Convent [level] to NSString.
*/
- (NSString *)levelDescription;
/**
Convent [date] to NSDate.
*/
- (NSDate *_Nullable)dateDescription;
/**
Init of the model.
*/
- (instancetype _Nonnull)initWithFile:(NSString *_Nullable)file lineNo:(NSInteger)lineNo function:(NSString *_Nullable)function level:(LLConfigLogLevel)level onEvent:(NSString *_Nullable)onEvent message:(NSString *_Nullable)message date:(NSString *)date launchDate:(NSString *)launchDate userIdentity:(NSString *_Nullable)userIdentity;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,83 @@
//
// LLLogModel.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLLogModel.h"
#import "LLFormatterTool.h"
#import "LLConfig.h"
#import "LLTool.h"
@interface LLLogModel ()
@property (nonatomic, strong) NSDate *dateDescription;
@end
@implementation LLLogModel
- (instancetype)initWithFile:(NSString *)file lineNo:(NSInteger)lineNo function:(NSString *)function level:(LLConfigLogLevel)level onEvent:(NSString *)onEvent message:(NSString *)message date:(NSString *)date launchDate:(NSString *)launchDate userIdentity:(NSString *)userIdentity {
if (self = [super init]) {
_file = [file copy];
_lineNo = lineNo;
_function = [function copy];
_level = level;
_event = [onEvent copy];
_message = [message copy];
_date = [date copy];
_launchDate = [launchDate copy];
_userIdentity = [userIdentity copy];
_identity = [date stringByAppendingString:[LLTool absolutelyIdentity]];
}
return self;
}
- (NSString *)levelDescription {
switch (self.level) {
case LLConfigLogLevelDefault:
return @"Default";
case LLConfigLogLevelAlert:
return @"Alert";
case LLConfigLogLevelWarning:
return @"Warning";
case LLConfigLogLevelError:
return @"Error";
}
return @"Unknown";
}
- (NSDate *)dateDescription {
if (!_dateDescription && self.date.length) {
_dateDescription = [LLFormatterTool dateFromString:self.date style:FormatterToolDateStyle1];
}
return _dateDescription;
}
- (NSString *)storageIdentity {
return self.identity;
}
- (NSString *)description {
return [NSString stringWithFormat:@"[LLLogModel] \n message:%@,\n file:%@,\n function:%@,\n lineNo:%ld,\n event:%@,\n level:%@,\n date:%@,\n launchDate:%@,\n userIdentity:%@,\n identity:%@",self.message,self.file,self.function,(long)self.lineNo,self.event,self.levelDescription,self.dateDescription,self.launchDate,self.userIdentity,self.identity];
}
@end

View File

@@ -0,0 +1,40 @@
//
// LLLog.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#ifndef LLLog_h
#define LLLog_h
#import "LLLogComponent.h"
#import "LLLogHelper.h"
#import "LLLogModel.h"
#import "LLLogDetailViewController.h"
#import "LLLogViewController.h"
#import "LLLogFilterView.h"
#import "LLLogWindow.h"
#import "LLLogCell.h"
#import "LLWindowManager+Log.h"
#endif /* LLLog_h */

View File

@@ -0,0 +1,33 @@
//
// LLLogComponent.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLComponent.h"
NS_ASSUME_NONNULL_BEGIN
/// Log function component.
@interface LLLogComponent : LLComponent
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,43 @@
//
// LLLogComponent.m
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLLogComponent.h"
#import "LLNavigationController.h"
#import "LLLogViewController.h"
#import "LLWindowManager+Log.h"
@implementation LLLogComponent
- (void)componentDidLoad:(NSDictionary *)data {
LLBaseWindow *window = [[LLWindowManager shared] visiableWindow];
if ([window isKindOfClass:[LLFunctionWindow class]]) {
LLNavigationController *nav = (LLNavigationController *)window.rootViewController;
[nav pushViewController:[[LLLogViewController alloc] init] animated:YES];
} else {
[[LLWindowManager shared] showWindow:[LLWindowManager logWindow] animated:YES];
}
}
@end

View File

@@ -0,0 +1,39 @@
//
// LLLogCell.h
//
// Copyright (c) 2018 LLDebugTool Software Foundation (https://github.com/HDB-Li/LLDebugTool)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#import "LLBaseTableViewCell.h"
@class LLLogModel;
NS_ASSUME_NONNULL_BEGIN
/// Log function cell.
@interface LLLogCell : LLBaseTableViewCell
/// Config cell with model.
/// @param model LLLogModel.
- (void)confirmWithModel:(LLLogModel *)model;
@end
NS_ASSUME_NONNULL_END

Some files were not shown because too many files have changed in this diff Show More