Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix get selected text failed #831

Merged
merged 4 commits into from
Mar 3, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 58 additions & 33 deletions Easydict/objc/EventMonitor/EZEventMonitor.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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];
}];
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}
Expand Down