summaryrefslogtreecommitdiffstats
path: root/QuickActions/QuickActionsPrefs/LSApplicationProxy+AltList.m
blob: ab6c2925f42b313a0148663166d81bf811822be4 (plain) (blame)
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
#import <Foundation/Foundation.h>
#import "LSApplicationProxy+AltList.h"

@implementation LSApplicationProxy (AltList)
// 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.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.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;
}
@end