Fix IME activation issues on macOS 12
We now let the Installer to call the TextInputSources API. Since macOS 12, users are prompted to allow enabling of third-party IMEs in Preferences.app the momemnt TISRegisterInputSource or TISEnableInputSource is called. By moving the activation to the Installer, a user will clearly see that it's the Installer that wants to enable the IME. In addition, we had to make necessary changes so that on macOS 12 and later, the Installer always enable the default input source. This is due to the observation that the kTISPropertyInputSourceIsEnabled becomes unreliable on macOS 12--it may be true even if the user has removed the input mode from their active input mode list in Preferences.app.
This commit is contained in:
parent
b85029dec1
commit
c1bea8c382
|
@ -33,6 +33,7 @@
|
|||
6A2E40F9253A6AA000D1AE1D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6A2E40F5253A69DA00D1AE1D /* Images.xcassets */; };
|
||||
6A38BC1515FC117A00A8A51F /* data.txt in Resources */ = {isa = PBXBuildFile; fileRef = 6A38BBF615FC117A00A8A51F /* data.txt */; };
|
||||
6A38BC2815FC158A00A8A51F /* InputMethodKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A38BC2715FC158A00A8A51F /* InputMethodKit.framework */; };
|
||||
6AB3620D274CA50700AC7547 /* OVInputSourceHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4ECA15FC0D6400ABF4B3 /* OVInputSourceHelper.m */; };
|
||||
6ACA41CD15FC1D7500935EF6 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A0D4EA615FC0D2D00ABF4B3 /* Cocoa.framework */; };
|
||||
6ACA41F915FC1D9000935EF6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6ACA41E915FC1D9000935EF6 /* AppDelegate.m */; };
|
||||
6ACA41FA15FC1D9000935EF6 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6ACA41EA15FC1D9000935EF6 /* InfoPlist.strings */; };
|
||||
|
@ -660,6 +661,7 @@
|
|||
files = (
|
||||
6ACA41F915FC1D9000935EF6 /* AppDelegate.m in Sources */,
|
||||
6A225A232367A1D700F685C6 /* ArchiveUtil.m in Sources */,
|
||||
6AB3620D274CA50700AC7547 /* OVInputSourceHelper.m in Sources */,
|
||||
6ACA41FF15FC1D9000935EF6 /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -1038,6 +1040,7 @@
|
|||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = ../;
|
||||
INFOPLIST_FILE = "Source/Installer/Installer-Info.plist";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
|
@ -1070,6 +1073,7 @@
|
|||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = ../;
|
||||
INFOPLIST_FILE = "Source/Installer/Installer-Info.plist";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "org.openvanilla.McBopomofo.${PRODUCT_NAME:rfc1034identifier}";
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#import "AppDelegate.h"
|
||||
#import <sys/mount.h>
|
||||
#import "OVInputSourceHelper.h"
|
||||
|
||||
static NSString *const kTargetBin = @"McBopomofo";
|
||||
static NSString *const kTargetType = @"app";
|
||||
|
@ -55,7 +56,6 @@ void RunAlertPanel(NSString *title, NSString *message, NSString *buttonTitle) {
|
|||
@synthesize progressSheet = _progressSheet;
|
||||
@synthesize progressIndicator = _progressIndicator;
|
||||
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||
{
|
||||
_installingVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:(id)kCFBundleVersionKey];
|
||||
|
@ -130,9 +130,9 @@ void RunAlertPanel(NSString *title, NSString *message, NSString *buttonTitle) {
|
|||
// Schedule the install action in runloop so that the sheet gets a change to dismiss itself.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (returnCode == NSModalResponseContinue) {
|
||||
[self installInputMethodWithWarning:NO];
|
||||
[self installInputMethodWithPreviousExists:YES previousVersionNotFullyDeactivatedWarning:NO];
|
||||
} else {
|
||||
[self installInputMethodWithWarning:YES];
|
||||
[self installInputMethodWithPreviousExists:YES previousVersionNotFullyDeactivatedWarning:YES];
|
||||
}
|
||||
});
|
||||
}];
|
||||
|
@ -143,7 +143,7 @@ void RunAlertPanel(NSString *title, NSString *message, NSString *buttonTitle) {
|
|||
}
|
||||
}
|
||||
|
||||
[self installInputMethodWithWarning:NO];
|
||||
[self installInputMethodWithPreviousExists:NO previousVersionNotFullyDeactivatedWarning:NO];
|
||||
}
|
||||
|
||||
- (void)timerTick:(NSTimer *)timer
|
||||
|
@ -162,7 +162,7 @@ void RunAlertPanel(NSString *title, NSString *message, NSString *buttonTitle) {
|
|||
}
|
||||
|
||||
|
||||
- (void)installInputMethodWithWarning:(BOOL)warning
|
||||
- (void)installInputMethodWithPreviousExists:(BOOL)previousVersionExists previousVersionNotFullyDeactivatedWarning:(BOOL)warning
|
||||
{
|
||||
// If the unzipped archive does not exist, this must be a dev-mode installer.
|
||||
NSString *targetBundle = [_archiveUtil unzipNotarizedArchive];
|
||||
|
@ -177,23 +177,80 @@ void RunAlertPanel(NSString *title, NSString *message, NSString *buttonTitle) {
|
|||
[NSApp terminate:self];
|
||||
}
|
||||
|
||||
NSArray *installArgs = [NSArray arrayWithObjects:@"install", nil];
|
||||
NSTask *installTask = [NSTask launchedTaskWithLaunchPath:[kTargetFullBinPartialPath stringByExpandingTildeInPath] arguments:installArgs];
|
||||
[installTask waitUntilExit];
|
||||
if ([installTask terminationStatus] != 0) {
|
||||
RunAlertPanel(NSLocalizedString(@"Install Failed", nil), NSLocalizedString(@"Cannot activate the input method.", nil), NSLocalizedString(@"Cancel", nil));
|
||||
[NSApp terminate:self];
|
||||
NSBundle *imeBundle = [NSBundle bundleWithPath:[kTargetPartialPath stringByExpandingTildeInPath]];
|
||||
NSCAssert(imeBundle != nil, @"Target bundle must exists");
|
||||
NSURL *imeBundleURL = imeBundle.bundleURL;
|
||||
NSString *imeIdentifier = imeBundle.bundleIdentifier;
|
||||
|
||||
TISInputSourceRef inputSource = [OVInputSourceHelper inputSourceForInputSourceID:imeIdentifier];
|
||||
|
||||
// if this IME name is not found in the list of available IMEs
|
||||
if (!inputSource) {
|
||||
NSLog(@"Registering input source %@ at %@.", imeIdentifier, imeBundleURL.absoluteString);
|
||||
// then register
|
||||
BOOL status = [OVInputSourceHelper registerInputSource:imeBundleURL];
|
||||
|
||||
if (!status) {
|
||||
// TODO: Localize.
|
||||
NSString *message = [NSString stringWithFormat:@"Fatal error: Cannot register input source %@ at %@.", imeIdentifier, imeBundleURL.absoluteString];
|
||||
RunAlertPanel(@"Fatal Error", message, @"Abort");
|
||||
[self endAppWithDelay];
|
||||
return;
|
||||
}
|
||||
|
||||
inputSource = [OVInputSourceHelper inputSourceForInputSourceID:imeIdentifier];
|
||||
// if it still doesn't register successfully, bail.
|
||||
if (!inputSource) {
|
||||
// TODO: Localize.
|
||||
NSString *message = [NSString stringWithFormat:@"Fatal error: Cannot find input source %@ after registration.", imeIdentifier];
|
||||
RunAlertPanel(@"Fatal Error", message, @"Abort");
|
||||
[self endAppWithDelay];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL isMacOS12OrAbove = NO;
|
||||
if (@available(macOS 12.0, *)) {
|
||||
NSLog(@"macOS 12 or later detected.");
|
||||
isMacOS12OrAbove = YES;
|
||||
} else {
|
||||
NSLog(@"Installer runs with the pre-macOS 12 flow.");
|
||||
}
|
||||
|
||||
// If the IME is not enabled, enable it. Also, unconditionally enable it on macOS 12.0+,
|
||||
// as the kTISPropertyInputSourceIsEnabled can still be true even if the IME is *not*
|
||||
// enabled in the user's current set of IMEs (which means the IME does not show up in
|
||||
// the user's input menu).
|
||||
BOOL mainInputSourceEnabled = [OVInputSourceHelper inputSourceEnabled:inputSource];
|
||||
if (!mainInputSourceEnabled || isMacOS12OrAbove) {
|
||||
|
||||
mainInputSourceEnabled = [OVInputSourceHelper enableInputSource:inputSource];
|
||||
if (mainInputSourceEnabled) {
|
||||
NSLog(@"Input method enabled: %@", imeIdentifier);
|
||||
} else {
|
||||
NSLog(@"Failed to enable input method: %@", imeIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
if (warning) {
|
||||
RunAlertPanel(NSLocalizedString(@"Attention", nil), NSLocalizedString(@"McBopomofo is upgraded, but please log out or reboot for the new version to be fully functional.", nil), NSLocalizedString(@"OK", nil));
|
||||
} else {
|
||||
// Only prompt a warning if pre-macOS 12. The flag is not indicative of anything meaningful due to the need of user intervention in Prefernces.app on macOS 12.
|
||||
if (!mainInputSourceEnabled && !isMacOS12OrAbove) {
|
||||
// TODO: Localize
|
||||
RunAlertPanel(@"Warning", @"Input method may not be fully enabled. Please check Preferences.app.", @"Continue");
|
||||
} else {
|
||||
RunAlertPanel(NSLocalizedString(@"Installation Successful", nil), NSLocalizedString(@"McBopomofo is ready to use.", nil), NSLocalizedString(@"OK", nil));
|
||||
}
|
||||
|
||||
[[NSApplication sharedApplication] performSelector:@selector(terminate:) withObject:self afterDelay:0.1];
|
||||
}
|
||||
|
||||
[self endAppWithDelay];
|
||||
}
|
||||
|
||||
- (void)endAppWithDelay
|
||||
{
|
||||
[[NSApplication sharedApplication] performSelector:@selector(terminate:) withObject:self afterDelay:0.1];
|
||||
}
|
||||
|
||||
- (IBAction)cancelAction:(id)sender
|
||||
{
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
+ (BOOL)inputSourceEnabled:(TISInputSourceRef)inInputSource;
|
||||
+ (BOOL)enableInputSource:(TISInputSourceRef)inInputSource;
|
||||
+ (BOOL)enableAllInputModesForInputSourceBundleID:(NSString *)inID;
|
||||
+ (BOOL)enableInputMode:(NSString *)modeID forInputSourceBundleID:(NSString *)bundleID;
|
||||
+ (BOOL)disableInputSource:(TISInputSourceRef)inInputSource;
|
||||
|
||||
// register (i.e. make available to Input Source tab in Language & Text Preferences)
|
||||
|
|
|
@ -90,6 +90,24 @@
|
|||
return enabled;
|
||||
}
|
||||
|
||||
+ (BOOL)enableInputMode:(NSString *)modeID forInputSourceBundleID:(NSString *)bundleID
|
||||
{
|
||||
for (id source in [self allInstalledInputSources]) {
|
||||
TISInputSourceRef inputSource = (__bridge TISInputSourceRef)source;
|
||||
NSString *inputSoureBundleID = (__bridge NSString *)TISGetInputSourceProperty(inputSource, kTISPropertyBundleID);
|
||||
NSString *inputSourceModeID = (NSString *)CFBridgingRelease(TISGetInputSourceProperty(inputSource, kTISPropertyInputModeID));
|
||||
|
||||
if ([modeID isEqual:inputSourceModeID] && [bundleID isEqual:inputSoureBundleID]) {
|
||||
BOOL enabled = [self enableInputSource:inputSource];
|
||||
NSLog(@"Attempt to enable input source of mode: %@, bundle ID: %@, result: %d", modeID, bundleID, enabled);
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"Failed to find any matching input source of mode: %@, bundle ID: %@", modeID, bundleID);
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)disableInputSource:(TISInputSourceRef)inInputSource
|
||||
{
|
||||
OSStatus status = TISDisableInputSource(inInputSource);
|
||||
|
|
Loading…
Reference in New Issue