diff --git a/Easydict/objc/EventMonitor/EZEventMonitor.m b/Easydict/objc/EventMonitor/EZEventMonitor.m index e1d424c29..3226aba49 100644 --- a/Easydict/objc/EventMonitor/EZEventMonitor.m +++ b/Easydict/objc/EventMonitor/EZEventMonitor.m @@ -316,10 +316,19 @@ - (void)getSelectedText:(BOOL)checkTextFrame completion:(void (^)(NSString *_Nul if (error) { // AppleScript may return timeout error if the selected text is in browser pop-up window, like Permanently remove my account in https://betterstack.com/settings/account MMLogError(@"Failed to get selected text from browser: %@", error); - [self handleForceGetSelectedTextOnAXError:kAXErrorNoValue completion:completion]; - } else { - completion(selectedText); + [self forceGetSelectedText:completion]; + return; } + + NSString *text = selectedText.trim; + if (text.length > 0) { + completion(text); + return; + } + + MMLogInfo(@"AppleScript get selected text is empty, try to use force get selected text for browser"); + + [self forceGetSelectedText:completion]; }); }]; return; @@ -329,7 +338,7 @@ - (void)getSelectedText:(BOOL)checkTextFrame completion:(void (^)(NSString *_Nul MMLogError(@"Failed to get text, kAXErrorAPIDisabled"); } - // 3. Try to use simulated key to get selected text. + // 3. Try to use force get selected text. [self handleForceGetSelectedTextOnAXError:axError completion:completion]; }]; } @@ -403,31 +412,36 @@ - (void)handleSelectedText:(NSString *)text { } -/// Get selected text by simulated key: Cmd + C +/// Get selected text by simulated key Cmd+C, and mute alert volume. - (void)getSelectedTextBySimulatedKey:(void (^)(NSString *_Nullable))completion { MMLogInfo(@"Get selected text by simulated key"); + self.selectTextType = EZSelectTextTypeSimulatedKey; + // Do not mute alert volume if already muting, avoid getting muted volume 0, since this method may be called multiple times when dragging window. if (!self.isMutingAlertVolume) { self.isMutingAlertVolume = YES; - // Mute alert volume to avoid alert. + // First, mute alert volume to avoid alert. [AppleScriptTask muteAlertVolumeWithCompletionHandler:^(NSInteger volume, NSError *error) { if (error) { MMLogError(@"Failed to mute alert volume: %@", error); } else { self.currentAlertVolume = volume; } + + // After muting alert volume, get selected text by simulated key. + [SharedUtilities getSelectedTextByShortcutCopyWithCompletionHandler:^(NSString *selectedText) { + MMLogInfo(@"Get selected text by simulated key success: %@", selectedText); + completion(selectedText); + }]; }]; } [self cancelDelayRecoverVolume]; - [self delayRecoverVolume]; - [SharedUtilities getSelectedTextByShortcutCopyWithCompletionHandler:^(NSString *selectedText) { - MMLogInfo(@"Get selected text by simulated key success: %@", selectedText); - completion(selectedText); - }]; + // Delay to recover volume, avoid alert volume if the user does not select text when simulating Cmd+C. + [self delayRecoverVolume]; } #pragma mark - Delay to recover volume @@ -556,29 +570,40 @@ - (BOOL)isAccessibilityEnabled { /// Check error type to use menu action copy or simulated key to get selected text. - (void)handleForceGetSelectedTextOnAXError:(AXError)axError completion:(void (^)(NSString *_Nullable))completion { if ([self shouldForceGetSelectedTextWithAXError:axError]) { - // Menu bar action copy is better than simulated key in most cases, such as WeChat, Telegram, etc, but it may be not stable, we need more test. - // TODO: Try to find a more stable way to get selected text, or combine both methods. - if (Configuration.shared.forceGetSelectedTextType == ForceGetSelectedTextTypeMenuBarActionCopy) { - self.selectTextType = EZSelectTextTypeMenuBarActionCopy; - [SharedUtilities getSelectedTextByMenuBarActionCopyWithCompletionHandler:^(NSString *text, NSError *error) { - if (error) { - MMLogError(@"Failed to get selected text by menu bar action copy: %@", error); - completion(nil); - } else { - MMLogInfo(@"Get selected text by menu bar action copy success: %@", text); - completion(text); - } - }]; - } else { - [self getSelectedTextBySimulatedKey:^(NSString *text) { - self.selectTextType = EZSelectTextTypeSimulatedKey; - completion(text); - }]; - } - return; + [self forceGetSelectedText:completion]; + } else { + completion(nil); } +} - completion(nil); +/// Force get selected text when Accessibility failed. +- (void)forceGetSelectedText:(void (^)(NSString *_Nullable))completion { + MMLogInfo(@"Use force get selected text"); + + // Menu bar action copy is better than simulated key in most cases, such as WeChat, Telegram, etc, but it may be not stable, some apps do not have copy menu item, like Billfish. + + // TODO: Try to find a more stable way to get selected text, or combine both methods. + + if (Configuration.shared.forceGetSelectedTextType == ForceGetSelectedTextTypeMenuBarActionCopy) { + self.selectTextType = EZSelectTextTypeMenuBarActionCopy; + [SharedUtilities getSelectedTextByMenuBarActionCopyWithCompletionHandler:^(NSString *_Nullable text, NSError *error) { + NSString *trimText = [text trim]; + if (trimText.length > 0) { + MMLogInfo(@"Get selected text by menu bar action copy success: %@", trimText); + completion(trimText); + return; + } + + if (error) { + MMLogError(@"Failed to get selected text by menu bar action copy: %@", error); + } else { + MMLogError(@"Get selected text by menu bar action copy is empty, try to use simulated key"); + } + [self getSelectedTextBySimulatedKey:completion]; + }]; + } else { + [self getSelectedTextBySimulatedKey:completion]; + } } /// Check if should force get selected text when Accessibility failed. @@ -683,7 +708,7 @@ - (BOOL)shouldForceGetSelectedTextWithAXError:(AXError)axError { return YES; } - MMLogInfo(@"Not use force get selected text: %d, %@", axError, application); + MMLogInfo(@"After check axError: %d, not use force get selected text: %@", axError, application); return NO; }