diff --git a/Natives/JavaLauncher.m b/Natives/JavaLauncher.m index fb455aab..576fccc2 100644 --- a/Natives/JavaLauncher.m +++ b/Natives/JavaLauncher.m @@ -129,6 +129,9 @@ int launchJVM(NSString *username, id launchTarget, int width, int height, int mi regLog("[JavaLauncher] Beginning JVM launch\n"); NSString *javaHome_pre = getPreference(@"java_home"); + if (![javaHome_pre hasPrefix:@"/"]) { + javaHome_pre = [NSString stringWithFormat:@"%s/jvm/%@", getenv("POJAV_DETECTEDJB") ? "/usr/lib" : getenv("BUNDLE_PATH"), javaHome_pre]; + } // We handle unset JAVA_HOME right there if (getSelectedJavaVersion() < minVersion) { diff --git a/Natives/LauncherPreferences.h b/Natives/LauncherPreferences.h index 89dab22d..711ba25e 100644 --- a/Natives/LauncherPreferences.h +++ b/Natives/LauncherPreferences.h @@ -4,13 +4,14 @@ #define JRE16_HOME_JB @"/usr/lib/jvm/java-16-openjdk" #define JRE17_HOME_JB @"/usr/lib/jvm/java-17-openjdk" -void loadPreferences(); +void loadPreferences(BOOL reset); id getPreference(NSString* key); NSMutableDictionary* getDictionary(NSString *type); int getJavaVersion(NSString* java); int getSelectedJavaVersion(); void setDefaultValueForPref(NSMutableDictionary *dict, NSString* key, id value); void setPreference(NSString* key, id value); +void resetWarnings(); CGRect getDefaultSafeArea(); BOOL getEntitlementValue(NSString *key); diff --git a/Natives/LauncherPreferences.m b/Natives/LauncherPreferences.m index 6505b5a4..d305d53e 100644 --- a/Natives/LauncherPreferences.m +++ b/Natives/LauncherPreferences.m @@ -78,14 +78,26 @@ void setPreference(NSString* key, id value) { [prefDict writeToFile:prefPath atomically:YES]; } -void loadPreferences() { +void fillDefaultWarningDict() { + setDefaultValueForPref(warnPrefDict, @"option_warn", @YES); + setDefaultValueForPref(warnPrefDict, @"local_warn", @YES); + setDefaultValueForPref(warnPrefDict, @"mem_warn", @YES); + setDefaultValueForPref(warnPrefDict, @"java_warn", @YES); + setDefaultValueForPref(warnPrefDict, @"demo_warn", @YES); + setDefaultValueForPref(warnPrefDict, @"jb_warn", @YES); + setDefaultValueForPref(warnPrefDict, @"customctrl_warn", @YES); + setDefaultValueForPref(warnPrefDict, @"int_warn", @YES); + setDefaultValueForPref(warnPrefDict, @"ram_unjb_warn", @YES); +} + +void loadPreferences(BOOL reset) { assert(getenv("POJAV_HOME")); prefPath = [@(getenv("POJAV_HOME")) stringByAppendingPathComponent:@"launcher_preferences.plist"]; NSFileManager *fileManager = [NSFileManager defaultManager]; prefDict = [NSMutableDictionary dictionaryWithContentsOfFile:prefPath]; - if (![fileManager fileExistsAtPath:prefPath] || [prefDict[@"reset_settings"] boolValue]) { + if (reset || ![fileManager fileExistsAtPath:prefPath]) { prefDict = [[NSMutableDictionary alloc] init]; envPrefDict = [[NSMutableDictionary alloc] init]; warnPrefDict = [[NSMutableDictionary alloc] init]; @@ -111,15 +123,7 @@ void loadPreferences() { setDefaultValueForPref(prefDict, @"arccapes_enable", @YES); setDefaultValueForPref(envPrefDict, @"java_home", @""); setDefaultValueForPref(envPrefDict, @"renderer", @"auto"); - setDefaultValueForPref(warnPrefDict, @"option_warn", @YES); - setDefaultValueForPref(warnPrefDict, @"local_warn", @YES); - setDefaultValueForPref(warnPrefDict, @"mem_warn", @YES); - setDefaultValueForPref(warnPrefDict, @"java_warn", @YES); - setDefaultValueForPref(warnPrefDict, @"demo_warn", @YES); - setDefaultValueForPref(warnPrefDict, @"jb_warn", @YES); - setDefaultValueForPref(warnPrefDict, @"customctrl_warn", @YES); - setDefaultValueForPref(warnPrefDict, @"int_warn", @YES); - setDefaultValueForPref(warnPrefDict, @"ram_unjb_warn", @YES); + fillDefaultWarningDict(); setDefaultValueForPref(prefDict, @"a7_allow", @NO); setDefaultValueForPref(prefDict, @"slideable_hotbar", @NO); setDefaultValueForPref(prefDict, @"virtmouse_enable", @NO); @@ -132,7 +136,8 @@ void loadPreferences() { setDefaultValueForPref(prefDict, @"disable_home_symlink", @YES); } - setDefaultValueForPref(prefDict, @"control_safe_area", NSStringFromCGRect(getDefaultSafeArea())); + // Migrate some prefs + setPreference(@"java_home", [getPreference(@"java_home") lastPathComponent]); prefDict[@"env_vars"] = envPrefDict; prefDict[@"warnings"] = warnPrefDict; @@ -140,6 +145,13 @@ void loadPreferences() { [prefDict writeToFile:prefPath atomically:YES]; } +void resetWarnings() { + [warnPrefDict removeAllObjects]; + fillDefaultWarningDict(); + prefDict[@"warnings"] = warnPrefDict; + [prefDict writeToFile:prefPath atomically:YES]; +} + CGRect getDefaultSafeArea() { CGRect defaultSafeArea = UIScreen.mainScreen.bounds; UIEdgeInsets insets = UIApplication.sharedApplication.windows.firstObject.safeAreaInsets; diff --git a/Natives/LauncherPreferencesViewController.m b/Natives/LauncherPreferencesViewController.m index 6eaf8afc..fef75ad3 100644 --- a/Natives/LauncherPreferencesViewController.m +++ b/Natives/LauncherPreferencesViewController.m @@ -550,9 +550,7 @@ int tempIndex; setPreference(@"renderer", rendererDict[textField.text]); setenv("POJAV_RENDERER", [getPreference(@"renderer") UTF8String], 1); } else if (textField.tag == TAG_JHOME) { - NSString *listPath = getenv("POJAV_DETECTEDJB") ? @"/usr/lib/jvm" : [NSString stringWithFormat:@"%s/jvm", getenv("BUNDLE_PATH")]; - setPreference(@"java_home", [NSString stringWithFormat:@"%@/%@", listPath, textField.text]); - setenv("JAVA_HOME", [getPreference(@"java_home") UTF8String], 1); + setPreference(@"java_home", textField.text); if (![textField.text containsString:JRE8_NAME_JB] && ![textField.text containsString:JRE8_NAME_SB] && [getPreference(@"java_warn") boolValue] == YES) { UIAlertController *javaAlert = [UIAlertController alertControllerWithTitle:@"Java version is not Java 8" message:@"Minecraft versions below 1.6, modded below 1.16.4, and the mod installer will not work unless you have Java 8 installed on your device." preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]; diff --git a/Natives/LauncherPreferencesViewController2.m b/Natives/LauncherPreferencesViewController2.m index f567e3c5..d069aa27 100644 --- a/Natives/LauncherPreferencesViewController2.m +++ b/Natives/LauncherPreferencesViewController2.m @@ -14,7 +14,7 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *); @property NSArray* prefSections; @property NSArray*>* prefContents; -@property CreateView typePickField, typeTextField, typeSlider, typeSwitch; +@property CreateView typeButton, typePickField, typeTextField, typeSlider, typeSwitch; @end @implementation LauncherPreferencesViewController2 @@ -26,7 +26,6 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *); [self initViewCreation]; self.tableView = [[TOInsetGroupedTableView alloc] init]; - self.tableView.allowsSelection = NO; self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive; self.prefSections = @[@"general", @"video", @"control", @"java"]; @@ -51,18 +50,23 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *); @"icon": @"eyeglasses", @"type": self.typeSwitch }, - @"reset_warnings": @{ - @"icon": @"exclamationmark.triangle", - @"type": self.typeSwitch - }, @"reset_settings": @{ @"icon": @"trash", - @"type": self.typeSwitch, - @"warnCondition": ^BOOL(UISwitch *view){ - return view.isOn; - }, - @"warnAlways": @YES - } + @"type": self.typeButton, + @"requestReload": @YES, + @"showConfirmPrompt": @(YES), + @"action": ^void(){ + loadPreferences(YES); + // TODO: reset UI states + } + }, + @"reset_warnings": @{ + @"icon": @"exclamationmark.triangle", + @"type": self.typeButton, + @"action": ^void(){ + resetWarnings(); + } + }, }, @{ // Video and renderer settings @"renderer": @{ @@ -129,11 +133,7 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *); @"icon": @"slider.horizontal.3", @"type": self.typeSwitch, //@"hidden": @(getenv("POJAV_DETECTEDJB") == NULL), - @"requestReload": @YES, - @"warnCondition": ^BOOL(UISwitch *view){ - return !view.isOn; - }, - @"warnAlways": @YES + @"requestReload": @YES }, @"allocated_memory": @{ @"icon": @"memorychip", @@ -146,7 +146,8 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *); @"warnAlways": @NO, @"warnCondition": ^BOOL(DBNumberedSlider *view){ return view.value >= NSProcessInfo.processInfo.physicalMemory / 1048576 * 0.4; - } + }, + @"warnKey": @"mem_warn" } } ]; @@ -169,13 +170,17 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *); if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"]; } + cell.selectionStyle = UITableViewCellSelectionStyleNone; + cell.textLabel.textColor = nil; NSString *key = self.prefContents[indexPath.section].allKeys[indexPath.row]; NSDictionary *item = self.prefContents[indexPath.section].allValues[indexPath.row]; CreateView createView = item[@"type"]; createView(cell, key, item); - objc_setAssociatedObject(cell.accessoryView, @"key", key, OBJC_ASSOCIATION_ASSIGN); - objc_setAssociatedObject(cell.accessoryView, @"item", item, OBJC_ASSOCIATION_ASSIGN); + if (cell.accessoryView) { + objc_setAssociatedObject(cell.accessoryView, @"key", key, OBJC_ASSOCIATION_ASSIGN); + objc_setAssociatedObject(cell.accessoryView, @"item", item, OBJC_ASSOCIATION_ASSIGN); + } // Set general properties if (@available(iOS 13.0, *)) { @@ -186,23 +191,34 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *); // Check if one has enable condition and call if it does BOOL(^checkEnable)(void) = item[@"enableCondition"]; cell.userInteractionEnabled = !checkEnable || checkEnable(); + cell.textLabel.enabled = cell.detailTextLabel.enabled = cell.userInteractionEnabled; return cell; } - (void)initViewCreation { __weak LauncherPreferencesViewController2 *weakSelf = self; + + self.typeButton = ^void(UITableViewCell *cell, NSString *key, NSDictionary *item) { + cell.accessoryView = nil; + cell.selectionStyle = UITableViewCellSelectionStyleGray; + cell.textLabel.textColor = UIColor.systemBlueColor; + }; + /* self.typePickField = ^void(UITableViewCell *cell, NSString *key, NSDictionary *item) { cell.accessoryView = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, cell.bounds.size.width / 2, cell.bounds.size.height)]; [(id)(cell.accessoryView) setText:getPreference(key)]; }; */ + self.typeTextField = ^void(UITableViewCell *cell, NSString *key, NSDictionary *item) { UITextField *view = [[UITextField alloc] initWithFrame:CGRectMake(0, 0, cell.bounds.size.width / 2.1, cell.bounds.size.height)]; + [view addTarget:view action:@selector(resignFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit]; view.autocorrectionType = UITextAutocorrectionTypeNo; view.autocapitalizationType = UITextAutocapitalizationTypeNone; view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin; + view.delegate = weakSelf; view.placeholder = NSLocalizedString(([NSString stringWithFormat:@"preference.placeholder.%@", key]), nil); view.returnKeyType = UIReturnKeyDone; view.text = getPreference(key); @@ -218,6 +234,7 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *); self.typeSlider = ^void(UITableViewCell *cell, NSString *key, NSDictionary *item) { DBNumberedSlider *view = [[DBNumberedSlider alloc] initWithFrame:CGRectMake(0, 0, cell.bounds.size.width / 2.1, cell.bounds.size.height)]; + [view addTarget:weakSelf action:@selector(sliderMoved:) forControlEvents:UIControlEventValueChanged]; view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin; view.minimumValue = [item[@"min"] intValue]; view.maximumValue = [item[@"max"] intValue]; @@ -234,27 +251,40 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *); }; } +- (void)showAlertOnView:(UIView *)view title:(NSString *)title message:(NSString *)message { + UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleActionSheet]; + alert.popoverPresentationController.sourceView = view; + alert.popoverPresentationController.sourceRect = view.bounds; + UIAlertAction *ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleCancel handler:nil]; + [alert addAction:ok]; + [self presentViewController:alert animated:YES completion:nil]; +} + - (void)checkWarn:(UIView *)view { NSString *key = objc_getAssociatedObject(view, @"key"); NSDictionary *item = objc_getAssociatedObject(view, @"item"); BOOL(^isWarnable)(UIView *) = item[@"warnCondition"]; NSString *warnKey = item[@"warnKey"]; - if (isWarnable && isWarnable(view) && ![getPreference(warnKey) boolValue]) { + if (isWarnable && isWarnable(view) && (!warnKey || [getPreference(warnKey) boolValue])) { if (warnKey) { - setPreference(warnKey, @YES); + setPreference(warnKey, @NO); } NSString *message = NSLocalizedString(([NSString stringWithFormat:@"preference.warn.%@", key]), nil); - UIAlertController *warnAlert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Warning", nil) message:message preferredStyle:UIAlertControllerStyleActionSheet]; - warnAlert.popoverPresentationController.sourceView = view; - warnAlert.popoverPresentationController.sourceRect = view.bounds; - UIAlertAction *ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleCancel handler:nil]; - [warnAlert addAction:ok]; - [self presentViewController:warnAlert animated:YES completion:nil]; + [self showAlertOnView:view title:NSLocalizedString(@"Warning", nil) message:message]; } } +#pragma mark Control event handlers + +- (void)sliderMoved:(DBNumberedSlider *)sender { + [self checkWarn:sender]; + NSString *key = objc_getAssociatedObject(sender, @"key"); + + setPreference(key, @(sender.value)); +} + - (void)switchChanged:(UISwitch *)sender { [self checkWarn:sender]; NSString *key = objc_getAssociatedObject(sender, @"key"); @@ -276,4 +306,55 @@ typedef void(^CreateView)(UITableViewCell *, NSString *, NSDictionary *); } } +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { + [tableView deselectRowAtIndexPath:indexPath animated:NO]; + + UITableViewCell *view = [self.tableView cellForRowAtIndexPath:indexPath]; + if (view.selectionStyle == UITableViewCellSelectionStyleNone) { + return; + } + + NSString *key = self.prefContents[indexPath.section].allKeys[indexPath.row]; + NSDictionary *item = self.prefContents[indexPath.section].allValues[indexPath.row]; + + if ([item[@"showConfirmPrompt"] boolValue]) { + NSString *title = NSLocalizedString(@"preference.title.confirm", nil); + NSString *message = NSLocalizedString(([NSString stringWithFormat:@"preference.title.confirm.%@", key]), nil); + UIAlertController *confirmAlert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleActionSheet]; + confirmAlert.popoverPresentationController.sourceView = view; + confirmAlert.popoverPresentationController.sourceRect = view.bounds; + UIAlertAction *ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { + [self tableView:tableView invokeActionAtIndexPath:indexPath]; + }]; + UIAlertAction *cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil]; + [confirmAlert addAction:cancel]; + [confirmAlert addAction:ok]; + [self presentViewController:confirmAlert animated:YES completion:nil]; + } else { + [self tableView:tableView invokeActionAtIndexPath:indexPath]; + } +} + +- (void)tableView:(UITableView *)tableView invokeActionAtIndexPath:(NSIndexPath *)indexPath { + NSString *key = self.prefContents[indexPath.section].allKeys[indexPath.row]; + NSDictionary *item = self.prefContents[indexPath.section].allValues[indexPath.row]; + + void(^invokeAction)(void) = item[@"action"]; + if (invokeAction) { + invokeAction(); + } + + UIView *view = [self.tableView cellForRowAtIndexPath:indexPath]; + NSString *title = NSLocalizedString(([NSString stringWithFormat:@"preference.title.done.%@", key]), nil); + //NSString *message = NSLocalizedString(([NSString stringWithFormat:@"preference.message.done.%@", key]), nil); + [self showAlertOnView:view title:title message:nil]; +} + +- (void)textFieldDidEndEditing:(UITextField *)sender { + [self checkWarn:sender]; + NSString *key = objc_getAssociatedObject(sender, @"key"); + + setPreference(key, sender.text); +} + @end diff --git a/Natives/LoginViewController.m b/Natives/LoginViewController.m index c88cf80b..bda5d3f5 100644 --- a/Natives/LoginViewController.m +++ b/Natives/LoginViewController.m @@ -21,6 +21,8 @@ #define TYPE_MICROSOFT 1 #define TYPE_OFFLINE 2 +extern NSMutableDictionary *prefDict; + #pragma mark - LoginViewController @interface LoginViewController () { } @@ -36,6 +38,8 @@ [super viewDidLoad]; setViewBackgroundColor(self.view); + setDefaultValueForPref(prefDict, @"control_safe_area", NSStringFromCGRect(getDefaultSafeArea())); + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(msaLoginCallback:) name:@"MSALoginCallback" object:nil]; CGRect screenBounds = [[UIScreen mainScreen] bounds]; diff --git a/Natives/en.lproj/Localizable.strings b/Natives/en.lproj/Localizable.strings index b42bb591..62d7865b 100644 --- a/Natives/en.lproj/Localizable.strings +++ b/Natives/en.lproj/Localizable.strings @@ -70,13 +70,20 @@ "preference.section.control" = "Control Settings"; "preference.section.java" = "Java Runtime Settings"; +"preference.title.confirm" = "Are you sure?"; + "preference.title.game_directory" = "Minecraft game directory"; "preference.title.home_symlink" = "Enable compatibility symlink"; "preference.title.check_sha" = "Enable asset verification when launching"; "preference.title.cosmetica" = "Enable Cosmetica integration"; + "preference.title.reset_warnings" = "Reset all warnings"; +"preference.title.done.reset_warnings" = "All warnings have been reset."; + "preference.title.reset_settings" = "Reset all settings"; -"preference.warn.reset_settings" = "The next time you open PojavLauncher, all of the default settings will be restored. If you did this by accident, you can turn this off back."; +"preference.title.confirm.reset_settings" = "This will remove all of your custom preferences, including resolution, button size, and Java arguments."; +"preference.title.done.reset_settings" = "All of the default settings have been restored."; + "preference.title.renderer" = "Game renderer"; "preference.title.resolution" = "Game resolution"; "preference.title.disable_gesture" = "Disable gestures"; @@ -91,7 +98,7 @@ // "preference.subtitle.java_home" = "Java 8 is used by default on Minecraft 1.16.5 and older. This switch forces old versions to use Java 17."; "preference.title.java_args" = "Custom java arguments"; "preference.title.auto_ram" = "Auto determine RAM amount"; -"preference.warn.auto_ram" = "Due to limitations in the operating system itself, high amount may cause instability. Proceed with caution."; "preference.title.allocated_memory" = "Allocated memory"; +"preference.warn.allocated_memory" = "Due to limitations in the operating system itself, this option may cause instability at this and higher values. Proceed with caution."; // sha check // home symlink diff --git a/Natives/main.m b/Natives/main.m index bb3e2b2b..722e5308 100644 --- a/Natives/main.m +++ b/Natives/main.m @@ -252,7 +252,7 @@ int main(int argc, char * argv[]) { init_hookFunctions(); - loadPreferences(); + loadPreferences(NO); init_setupResolvConf(); init_setupMultiDir(); init_setupLauncherProfiles();