-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMuteLockPolicy.m
More file actions
190 lines (148 loc) · 5.34 KB
/
MuteLockPolicy.m
File metadata and controls
190 lines (148 loc) · 5.34 KB
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
184
185
186
187
188
189
190
//
// MuteLockPolicy.m
// MuteLock
//
// Created by Yousef on 8/27/2025.
//
#import "MuteLockPolicy.h"
#import <notify.h>
@interface MuteLockPolicy ()
@property (nonatomic, strong) NSDictionary *preferences;
@property (nonatomic, assign) int notifyToken;
@property (nonatomic, strong) dispatch_source_t unlockTimer;
@end
static NSDictionary *_cachedPreferences = nil;
static dispatch_once_t _prefsLoadToken;
static NSDictionary *MLReadPreferences(void) {
dispatch_once(&_prefsLoadToken, ^{
_cachedPreferences = [NSDictionary dictionaryWithContentsOfFile:kMuteLockPrefsPath] ?: @{};
});
return _cachedPreferences ?: @{};
}
static void MLResetPreferencesCache(void) {
_prefsLoadToken = 0;
_cachedPreferences = nil;
}
@implementation MuteLockPolicy
#pragma mark - Singleton
+ (instancetype)sharedInstance {
static MuteLockPolicy *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[MuteLockPolicy alloc] init];
});
return instance;
}
- (instancetype)init {
self = [super init];
if (self) {
[self reloadState];
[self registerForNotifications];
}
return self;
}
#pragma mark - Notifications
- (void)registerForNotifications {
__weak typeof(self) weakSelf = self;
notify_register_dispatch(kMuteLockNotifyStateChanged, &_notifyToken, dispatch_get_main_queue(), ^(int token) {
MLResetPreferencesCache();
[weakSelf reloadState];
});
static int prefsToken;
notify_register_dispatch(kMuteLockNotifyPrefsChanged, &prefsToken, dispatch_get_main_queue(), ^(int token) {
MLResetPreferencesCache();
[weakSelf reloadState];
});
}
#pragma mark - State Loading
- (void)reloadState {
BOOL prefsFileExists = [[NSFileManager defaultManager] fileExistsAtPath:kMuteLockPrefsPath];
MLResetPreferencesCache();
NSMutableDictionary *prefs = [MLReadPreferences() mutableCopy] ?: [NSMutableDictionary dictionary];
if (!prefs[kMuteLockEnabled]) prefs[kMuteLockEnabled] = prefsFileExists ? @NO : @YES;
if (!prefs[kMuteLockCameraLocked]) prefs[kMuteLockCameraLocked] = @YES;
if (!prefs[kMuteLockMicLocked]) prefs[kMuteLockMicLocked] = @YES;
self.preferences = prefs;
[self checkAndExpireUnlock];
}
#pragma mark - Temporary Unlock Timer
- (void)checkAndExpireUnlock {
if (![self.preferences[kMuteLockTempUnlockActive] boolValue]) {
[self cancelUnlockTimer];
return;
}
NSNumber *expiryNum = self.preferences[kMuteLockTempUnlockExpiry];
if (!expiryNum) {
[self lockNow];
return;
}
NSTimeInterval remaining = [expiryNum doubleValue] - [[NSDate date] timeIntervalSince1970];
if (remaining <= 0) {
[self lockNow];
} else {
[self scheduleUnlockExpiryTimer:remaining];
}
}
- (void)scheduleUnlockExpiryTimer:(NSTimeInterval)delay {
[self cancelUnlockTimer];
self.unlockTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(self.unlockTimer,
dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)),
DISPATCH_TIME_FOREVER,
NSEC_PER_SEC);
__weak typeof(self) weakSelf = self;
dispatch_source_set_event_handler(self.unlockTimer, ^{
[weakSelf lockNow];
});
dispatch_resume(self.unlockTimer);
}
- (void)cancelUnlockTimer {
if (self.unlockTimer) {
dispatch_source_cancel(self.unlockTimer);
self.unlockTimer = nil;
}
}
- (void)lockNow {
[self cancelUnlockTimer];
NSMutableDictionary *prefs = [self.preferences mutableCopy];
prefs[kMuteLockTempUnlockActive] = @NO;
[prefs removeObjectForKey:kMuteLockTempUnlockExpiry];
[prefs writeToFile:kMuteLockPrefsPath atomically:YES];
_preferences = prefs;
MLResetPreferencesCache();
notify_post(kMuteLockNotifyStateChanged);
}
#pragma mark - State Properties
- (MuteLockState)currentState {
if (![self.preferences[kMuteLockEnabled] boolValue]) return MuteLockStateDisabled;
if ([self isTemporarilyUnlocked]) return MuteLockStateTemporarilyUnlocked;
return MuteLockStateLocked;
}
- (BOOL)isCameraLocked {
return [self.preferences[kMuteLockCameraLocked] boolValue];
}
- (BOOL)isMicrophoneLocked {
return [self.preferences[kMuteLockMicLocked] boolValue];
}
- (BOOL)isTemporarilyUnlocked {
if (![self.preferences[kMuteLockTempUnlockActive] boolValue]) return NO;
NSDate *expiry = self.unlockExpiryDate;
return expiry && [expiry timeIntervalSinceNow] > 0;
}
- (NSDate *)unlockExpiryDate {
NSNumber *expiryTimestamp = self.preferences[kMuteLockTempUnlockExpiry];
return expiryTimestamp ? [NSDate dateWithTimeIntervalSince1970:[expiryTimestamp doubleValue]] : nil;
}
- (BOOL)shouldBlockCameraForBundleID:(NSString *)bundleID {
if (self.currentState == MuteLockStateDisabled) return NO;
if (!self.isCameraLocked) return NO;
if (self.currentState == MuteLockStateTemporarilyUnlocked) return NO;
return YES;
}
- (BOOL)shouldBlockMicrophoneForBundleID:(NSString *)bundleID {
if (self.currentState == MuteLockStateDisabled) return NO;
if (!self.isMicrophoneLocked) return NO;
if (self.currentState == MuteLockStateTemporarilyUnlocked) return NO;
return YES;
}
@end