Path: blob/main/Natives/LauncherMenuViewController.m
589 views
#import "authenticator/BaseAuthenticator.h"1#import "AccountListViewController.h"2#import "AFNetworking.h"3#import "ALTServerConnection.h"4#import "LauncherNavigationController.h"5#import "LauncherMenuViewController.h"6#import "LauncherNewsViewController.h"7#import "LauncherPreferences.h"8#import "LauncherPreferencesViewController.h"9#import "LauncherProfilesViewController.h"10#import "PLProfiles.h"11#import "UIButton+AFNetworking.h"12#import "UIImageView+AFNetworking.h"13#import "UIKit+hook.h"14#import "ios_uikit_bridge.h"15#import "utils.h"1617#include <dlfcn.h>1819@implementation LauncherMenuCustomItem2021+ (LauncherMenuCustomItem *)title:(NSString *)title imageName:(NSString *)imageName action:(id)action {22LauncherMenuCustomItem *item = [[LauncherMenuCustomItem alloc] init];23item.title = title;24item.imageName = imageName;25item.action = action;26return item;27}2829+ (LauncherMenuCustomItem *)vcClass:(Class)class {30id vc = [class new];31LauncherMenuCustomItem *item = [[LauncherMenuCustomItem alloc] init];32item.title = [vc title];33item.imageName = [vc imageName];34// View controllers are put into an array to keep its state35item.vcArray = @[vc];36return item;37}3839@end4041@interface LauncherMenuViewController()42@property(nonatomic) NSMutableArray<LauncherMenuCustomItem*> *options;43@property(nonatomic) UILabel *statusLabel;44@property(nonatomic) int lastSelectedIndex;45@end4647@implementation LauncherMenuViewController4849#define contentNavigationController ((LauncherNavigationController *)self.splitViewController.viewControllers[1])5051- (void)viewDidLoad {52[super viewDidLoad];5354self.isInitialVc = YES;5556UIImageView *titleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"AppLogo"]];57[titleView setContentMode:UIViewContentModeScaleAspectFit];58self.navigationItem.titleView = titleView;59[titleView sizeToFit];6061self.options = @[62[LauncherMenuCustomItem vcClass:LauncherNewsViewController.class],63[LauncherMenuCustomItem vcClass:LauncherProfilesViewController.class],64[LauncherMenuCustomItem vcClass:LauncherPreferencesViewController.class],65].mutableCopy;66if (realUIIdiom != UIUserInterfaceIdiomTV) {67[self.options addObject:(id)[LauncherMenuCustomItem68title:localize(@"launcher.menu.custom_controls", nil)69imageName:@"MenuCustomControls" action:^{70[contentNavigationController performSelector:@selector(enterCustomControls)];71}]];72}73[self.options addObject:74(id)[LauncherMenuCustomItem75title:localize(@"launcher.menu.execute_jar", nil)76imageName:@"MenuInstallJar" action:^{77[contentNavigationController performSelector:@selector(enterModInstaller)];78}]];7980// TODO: Finish log-uploading service integration81[self.options addObject:82(id)[LauncherMenuCustomItem83title:localize(@"login.menu.sendlogs", nil)84imageName:@"square.and.arrow.up" action:^{85NSString *latestlogPath = [NSString stringWithFormat:@"file://%s/latestlog.old.txt", getenv("POJAV_HOME")];86NSLog(@"Path is %@", latestlogPath);87UIActivityViewController *activityVC;88if (realUIIdiom != UIUserInterfaceIdiomTV) {89activityVC = [[UIActivityViewController alloc]90initWithActivityItems:@[[NSURL URLWithString:latestlogPath]]91applicationActivities:nil];92} else {93dlopen("/System/Library/PrivateFrameworks/SharingUI.framework/SharingUI", RTLD_GLOBAL);94activityVC =95[[NSClassFromString(@"SFAirDropSharingViewControllerTV") alloc]96performSelector:@selector(initWithSharingItems:)97withObject:@[[NSURL URLWithString:latestlogPath]]];98}99activityVC.popoverPresentationController.sourceView = titleView;100activityVC.popoverPresentationController.sourceRect = titleView.bounds;101[self presentViewController:activityVC animated:YES completion:nil];102}]];103104NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];105dateFormatter.dateFormat = @"MM-dd";106NSString* date = [dateFormatter stringFromDate:NSDate.date];107if([date isEqualToString:@"06-29"] || [date isEqualToString:@"06-30"] || [date isEqualToString:@"07-01"]) {108[self.options addObject:(id)[LauncherMenuCustomItem109title:@"Technoblade never dies!"110imageName:@"" action:^{111openLink(self, [NSURL URLWithString:@"https://youtu.be/DPMluEVUqS0"]);112}]];113}114115self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;116117self.navigationController.toolbarHidden = NO;118UIActivityIndicatorViewStyle indicatorStyle = UIActivityIndicatorViewStyleMedium;119UIActivityIndicatorView *toolbarIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:indicatorStyle];120[toolbarIndicator startAnimating];121self.toolbarItems = @[122[[UIBarButtonItem alloc] initWithCustomView:toolbarIndicator],123[[UIBarButtonItem alloc] init]124];125self.toolbarItems[1].tintColor = UIColor.labelColor;126127// Setup the account button128self.accountBtnItem = [self drawAccountButton];129130[self updateAccountInfo];131132NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];133[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionNone];134[self tableView:self.tableView didSelectRowAtIndexPath:indexPath];135136if (getEntitlementValue(@"get-task-allow")) {137[self displayProgress:localize(@"login.jit.checking", nil)];138if (isJITEnabled(false)) {139[self displayProgress:localize(@"login.jit.enabled", nil)];140[self displayProgress:nil];141} else {142[self enableJITWithAltKit];143}144} else if (!NSProcessInfo.processInfo.macCatalystApp && !getenv("SIMULATOR_DEVICE_NAME")) {145[self displayProgress:localize(@"login.jit.fail", nil)];146[self displayProgress:nil];147UIAlertController* alert = [UIAlertController alertControllerWithTitle:localize(@"login.jit.fail.title", nil)148message:localize(@"login.jit.fail.description_unsupported", nil)149preferredStyle:UIAlertControllerStyleAlert];150UIAlertAction* okAction = [UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleDefault handler:^(id action){151exit(-1);152}];153[alert addAction:okAction];154[self presentViewController:alert animated:YES completion:nil];155}156}157158- (void)viewWillAppear:(BOOL)animated {159[super viewWillAppear:animated];160[self restoreHighlightedSelection];161}162163- (UIBarButtonItem *)drawAccountButton {164if (!self.accountBtnItem) {165self.accountButton = [UIButton buttonWithType:UIButtonTypeCustom];166[self.accountButton addTarget:self action:@selector(selectAccount:) forControlEvents:UIControlEventPrimaryActionTriggered];167self.accountButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;168169self.accountButton.titleEdgeInsets = UIEdgeInsetsMake(0, 4, 0, -4);170self.accountButton.imageView.contentMode = UIViewContentModeScaleAspectFit;171self.accountButton.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;172self.accountBtnItem = [[UIBarButtonItem alloc] initWithCustomView:self.accountButton];173}174175[self updateAccountInfo];176177return self.accountBtnItem;178}179180- (void)restoreHighlightedSelection {181// Restore the selected row when the view appears again182NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.lastSelectedIndex inSection:0];183[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];184}185186- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section187{188return self.options.count;189}190191- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath192{193UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];194if (cell == nil) {195cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];196}197198cell.textLabel.text = [self.options[indexPath.row] title];199200UIImage *origImage = [UIImage systemImageNamed:[self.options[indexPath.row]201performSelector:@selector(imageName)]];202if (origImage) {203UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:CGSizeMake(40, 40)];204UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext*_Nonnull myContext) {205CGFloat scaleFactor = 40/origImage.size.height;206[origImage drawInRect:CGRectMake(20 - origImage.size.width*scaleFactor/2, 0, origImage.size.width*scaleFactor, 40)];207}];208cell.imageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];209}210211if (cell.imageView.image == nil) {212cell.imageView.layer.magnificationFilter = kCAFilterNearest;213cell.imageView.layer.minificationFilter = kCAFilterNearest;214cell.imageView.image = [UIImage imageNamed:[self.options[indexPath.row]215performSelector:@selector(imageName)]];216cell.imageView.image = [cell.imageView.image _imageWithSize:CGSizeMake(40, 40)];217}218return cell;219}220221- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath222{223LauncherMenuCustomItem *selected = self.options[indexPath.row];224225if (selected.action != nil) {226[self restoreHighlightedSelection];227((LauncherMenuCustomItem *)selected).action();228} else {229if(self.isInitialVc) {230self.isInitialVc = NO;231} else {232self.options[self.lastSelectedIndex].vcArray = contentNavigationController.viewControllers;233[contentNavigationController setViewControllers:selected.vcArray animated:NO];234self.lastSelectedIndex = indexPath.row;235}236selected.vcArray[0].navigationItem.rightBarButtonItem = self.accountBtnItem;237selected.vcArray[0].navigationItem.leftBarButtonItem = self.splitViewController.displayModeButtonItem;238selected.vcArray[0].navigationItem.leftItemsSupplementBackButton = true;239}240}241242- (void)selectAccount:(UIButton *)sender {243AccountListViewController *vc = [[AccountListViewController alloc] init];244vc.whenDelete = ^void(NSString* name) {245if ([name isEqualToString:getPrefObject(@"internal.selected_account")]) {246BaseAuthenticator.current = nil;247setPrefObject(@"internal.selected_account", @"");248[self updateAccountInfo];249}250};251vc.whenItemSelected = ^void() {252setPrefObject(@"internal.selected_account", BaseAuthenticator.current.authData[@"username"]);253[self updateAccountInfo];254if (sender != self.accountButton) {255// Called from the play button, so call back to continue256[sender sendActionsForControlEvents:UIControlEventPrimaryActionTriggered];257}258};259vc.modalPresentationStyle = UIModalPresentationPopover;260vc.preferredContentSize = CGSizeMake(350, 250);261262UIPopoverPresentationController *popoverController = vc.popoverPresentationController;263popoverController.sourceView = sender;264popoverController.sourceRect = sender.bounds;265popoverController.permittedArrowDirections = UIPopoverArrowDirectionAny;266popoverController.delegate = vc;267[self presentViewController:vc animated:YES completion:nil];268}269270- (void)updateAccountInfo {271NSDictionary *selected = BaseAuthenticator.current.authData;272CGSize size = CGSizeMake(contentNavigationController.view.frame.size.width, contentNavigationController.view.frame.size.height);273274if (selected == nil) {275if((size.width / 3) > 200) {276[self.accountButton setAttributedTitle:[[NSAttributedString alloc] initWithString:localize(@"login.option.select", nil)] forState:UIControlStateNormal];277} else {278[self.accountButton setAttributedTitle:(NSAttributedString *)@"" forState:UIControlStateNormal];279}280[self.accountButton setImage:[UIImage imageNamed:@"DefaultAccount"] forState:UIControlStateNormal];281[self.accountButton sizeToFit];282return;283}284285// Remove the prefix "Demo." if there is286BOOL isDemo = [selected[@"username"] hasPrefix:@"Demo."];287NSMutableAttributedString *title = [[NSMutableAttributedString alloc] initWithString:[selected[@"username"] substringFromIndex:(isDemo?5:0)]];288289// Check if we're switching between demo and full mode290BOOL shouldUpdateProfiles = (getenv("DEMO_LOCK")!=NULL) != isDemo;291292// Reset states293unsetenv("DEMO_LOCK");294setenv("POJAV_GAME_DIR", [NSString stringWithFormat:@"%s/Library/Application Support/minecraft", getenv("POJAV_HOME")].UTF8String, 1);295296id subtitle;297if (isDemo) {298subtitle = localize(@"login.option.demo", nil);299setenv("DEMO_LOCK", "1", 1);300setenv("POJAV_GAME_DIR", [NSString stringWithFormat:@"%s/.demo", getenv("POJAV_HOME")].UTF8String, 1);301} else if (selected[@"xboxGamertag"] == nil) {302subtitle = localize(@"login.option.local", nil);303} else {304// Display the Xbox gamertag for online accounts305subtitle = selected[@"xboxGamertag"];306}307308subtitle = [[NSAttributedString alloc] initWithString:subtitle attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12]}];309[title appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n" attributes:nil]];310[title appendAttributedString:subtitle];311312if((size.width / 3) > 200) {313[self.accountButton setAttributedTitle:title forState:UIControlStateNormal];314} else {315[self.accountButton setAttributedTitle:(NSAttributedString *)@"" forState:UIControlStateNormal];316}317318// TODO: Add caching mechanism for profile pictures319NSURL *url = [NSURL URLWithString:[selected[@"profilePicURL"] stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"]];320UIImage *placeholder = [UIImage imageNamed:@"DefaultAccount"];321[self.accountButton setImageForState:UIControlStateNormal withURL:url placeholderImage:placeholder];322[self.accountButton.imageView setImageWithURL:url placeholderImage:placeholder];323[self.accountButton sizeToFit];324325// Update profiles and local version list if needed326if (shouldUpdateProfiles) {327[contentNavigationController fetchLocalVersionList];328[contentNavigationController performSelector:@selector(reloadProfileList)];329}330331// Update tableView whenever we have332UITableViewController *tableVC = contentNavigationController.viewControllers.lastObject;333if ([tableVC isKindOfClass:UITableViewController.class]) {334[tableVC.tableView reloadData];335}336}337338- (void)displayProgress:(NSString *)status {339if (status == nil) {340[(UIActivityIndicatorView *)self.toolbarItems[0].customView stopAnimating];341} else {342self.toolbarItems[1].title = status;343}344}345346- (void)enableJITWithAltKit {347[ALTServerManager.sharedManager startDiscovering];348[ALTServerManager.sharedManager autoconnectWithCompletionHandler:^(ALTServerConnection *connection, NSError *error) {349if (error) {350NSLog(@"[AltKit] Could not auto-connect to server. %@", error.localizedRecoverySuggestion);351[self displayProgress:localize(@"login.jit.fail", nil)];352[self displayProgress:nil];353}354[connection enableUnsignedCodeExecutionWithCompletionHandler:^(BOOL success, NSError *error) {355if (success) {356NSLog(@"[AltKit] Successfully enabled JIT compilation!");357[ALTServerManager.sharedManager stopDiscovering];358[self displayProgress:localize(@"login.jit.enabled", nil)];359[self displayProgress:nil];360} else {361NSLog(@"[AltKit] Error enabling JIT: %@", error.localizedRecoverySuggestion);362[self displayProgress:localize(@"login.jit.fail", nil)];363[self displayProgress:nil];364}365[connection disconnect];366}];367}];368}369370@end371372373