-
Notifications
You must be signed in to change notification settings - Fork 24
/
LSApplicationProxy+AltList.m
183 lines (154 loc) · 5.1 KB
/
LSApplicationProxy+AltList.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#import <Foundation/Foundation.h>
#import "LSApplicationProxy+AltList.h"
@implementation LSApplicationProxy (AltList)
- (BOOL)atl_isSystemApplication
{
return [self.applicationType isEqualToString:@"System"] && ![self atl_isHidden];
}
- (BOOL)atl_isUserApplication
{
return [self.applicationType isEqualToString:@"User"] && ![self atl_isHidden];
}
// the tag " hidden " is also valid, so we need to check if any strings contain "hidden" instead
BOOL tagArrayContainsTag(NSArray* tagArr, NSString* tag)
{
if(!tagArr || !tag) return NO;
__block BOOL found = NO;
[tagArr enumerateObjectsUsingBlock:^(NSString* tagToCheck, NSUInteger idx, BOOL* stop)
{
if(![tagToCheck isKindOfClass:[NSString class]])
{
return;
}
if([tagToCheck rangeOfString:tag options:0].location != NSNotFound)
{
found = YES;
*stop = YES;
}
}];
return found;
}
// always returns NO on iOS 7
- (BOOL)atl_isHidden
{
NSArray* appTags;
NSArray* recordAppTags;
NSArray* sbAppTags;
BOOL launchProhibited = NO;
if([self respondsToSelector:@selector(correspondingApplicationRecord)])
{
// On iOS 14, self.appTags is always empty but the application record still has the correct ones
LSApplicationRecord* record = [self correspondingApplicationRecord];
recordAppTags = record.appTags;
launchProhibited = record.launchProhibited;
}
if([self respondsToSelector:@selector(appTags)])
{
appTags = self.appTags;
}
if(!launchProhibited && [self respondsToSelector:@selector(isLaunchProhibited)])
{
launchProhibited = self.launchProhibited;
}
NSURL* bundleURL = self.bundleURL;
if(bundleURL && [bundleURL checkResourceIsReachableAndReturnError:nil])
{
NSBundle* bundle = [NSBundle bundleWithURL:bundleURL];
sbAppTags = [bundle objectForInfoDictionaryKey:@"SBAppTags"];
}
BOOL isWebApplication = ([self.atl_bundleIdentifier rangeOfString:@"com.apple.webapp" options:NSCaseInsensitiveSearch].location != NSNotFound);
return tagArrayContainsTag(appTags, @"hidden") || tagArrayContainsTag(recordAppTags, @"hidden") || tagArrayContainsTag(sbAppTags, @"hidden") || isWebApplication || launchProhibited;
}
// Getting the display name is slow (up to 2ms) because it uses an IPC call
// this stacks up if you do it for every single application
// This method provides a faster way (around 0.5ms) to get the display name
// This reduces the overall time needed to sort the applications from ~230 to ~120ms on my test device
- (NSString*)atl_fastDisplayName
{
NSString* cachedDisplayName = [self valueForKey:@"_localizedName"];
if(cachedDisplayName && ![cachedDisplayName isEqualToString:@""])
{
return cachedDisplayName;
}
NSString* localizedName;
NSURL* bundleURL = self.bundleURL;
if(!bundleURL || ![bundleURL checkResourceIsReachableAndReturnError:nil])
{
localizedName = self.localizedName;
}
else
{
NSBundle* bundle = [NSBundle bundleWithURL:bundleURL];
localizedName = [bundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
if(![localizedName isKindOfClass:[NSString class]]) localizedName = nil;
if(!localizedName || [localizedName isEqualToString:@""])
{
localizedName = [bundle objectForInfoDictionaryKey:@"CFBundleName"];
if(![localizedName isKindOfClass:[NSString class]]) localizedName = nil;
if(!localizedName || [localizedName isEqualToString:@""])
{
localizedName = [bundle objectForInfoDictionaryKey:@"CFBundleExecutable"];
if(![localizedName isKindOfClass:[NSString class]]) localizedName = nil;
if(!localizedName || [localizedName isEqualToString:@""])
{
//last possible fallback: use slow IPC call
localizedName = self.localizedName;
}
}
}
}
[self setValue:localizedName forKey:@"_localizedName"];
return localizedName;
}
- (NSString*)atl_nameToDisplay
{
NSString* localizedName = [self atl_fastDisplayName];
if([self.atl_bundleIdentifier rangeOfString:@"carplay" options:NSCaseInsensitiveSearch].location != NSNotFound)
{
if([localizedName rangeOfString:@"carplay" options:NSCaseInsensitiveSearch range:NSMakeRange(0, localizedName.length) locale:[NSLocale currentLocale]].location == NSNotFound)
{
return [localizedName stringByAppendingString:@" (CarPlay)"];
}
}
return localizedName;
}
-(id)atl_bundleIdentifier
{
// iOS 8-14
if([self respondsToSelector:@selector(bundleIdentifier)])
{
return [self bundleIdentifier];
}
// iOS 7
else
{
return [self applicationIdentifier];
}
}
- (NSString *)atl_shortVersionString {
NSString *version = self.shortVersionString;
if (version == nil || [version isEqualToString:@""]) {
version = @"1.0";
}
return version;
}
@end
@implementation LSApplicationWorkspace (AltList)
- (NSArray*)atl_allInstalledApplications
{
if(![self respondsToSelector:@selector(enumerateApplicationsOfType:block:)])
{
return [self allInstalledApplications];
}
NSMutableArray* installedApplications = [NSMutableArray new];
[self enumerateApplicationsOfType:0 block:^(LSApplicationProxy* appProxy)
{
[installedApplications addObject:appProxy];
}];
[self enumerateApplicationsOfType:1 block:^(LSApplicationProxy* appProxy)
{
[installedApplications addObject:appProxy];
}];
return installedApplications;
}
@end