Files
PojavLauncher_iOS/Natives/AccountListViewController.m
T
Eilionoir Tunnicliff dc22673b06 Remove iOS 12/13 supporting code + Alderis
PojavLauncher 3.0 will require heavy modifications in order to work on those two iOS versions now.
2023-05-08 11:15:52 -04:00

282 lines
12 KiB
Objective-C

#import <AuthenticationServices/AuthenticationServices.h>
#import "authenticator/BaseAuthenticator.h"
#import "AccountListViewController.h"
#import "AFNetworking.h"
#import "LauncherPreferences.h"
#import "UIImageView+AFNetworking.h"
#import "ios_uikit_bridge.h"
#import "utils.h"
@interface ATableViewCell : UITableViewCell
@end
@implementation ATableViewCell
@end
@interface AccountListViewController()<ASWebAuthenticationPresentationContextProviding>
@property(nonatomic, strong) NSMutableArray *accountList;
@property(nonatomic) ASWebAuthenticationSession *authVC;
@end
@implementation AccountListViewController
- (void)viewDidLoad {
[super viewDidLoad];
if (self.accountList == nil) {
self.accountList = [NSMutableArray array];
} else {
[self.accountList removeAllObjects];
}
// List accounts
NSString *listPath = [NSString stringWithFormat:@"%s/accounts", getenv("POJAV_HOME")];
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *files = [fm contentsOfDirectoryAtPath:listPath error:nil];
for(NSString *file in files) {
NSString *path = [listPath stringByAppendingPathComponent:file];
BOOL isDir = NO;
[fm fileExistsAtPath:path isDirectory:(&isDir)];
if(!isDir && [file hasSuffix:@".json"]) {
[self.accountList addObject:parseJSONFromFile(path)];
}
}
[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.accountList.count + 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"];
}
if (indexPath.row == self.accountList.count) {
cell.imageView.image = [UIImage imageNamed:@"IconAdd"];
cell.textLabel.text = localize(@"login.option.add", nil);
return cell;
}
NSDictionary *selected = self.accountList[indexPath.row];
// By default, display the saved username
cell.textLabel.text = selected[@"username"];
if ([selected[@"username"] hasPrefix:@"Demo."]) {
// Remove the prefix "Demo."
cell.textLabel.text = [selected[@"username"] substringFromIndex:5];
cell.detailTextLabel.text = localize(@"login.option.demo", nil);
} else if (selected[@"xboxGamertag"] == nil) {
cell.detailTextLabel.text = localize(@"login.option.local", nil);
} else {
// Display the Xbox gamertag for online accounts
cell.detailTextLabel.text = selected[@"xboxGamertag"];
}
cell.imageView.contentMode = UIViewContentModeCenter;
[cell.imageView setImageWithURL:[NSURL URLWithString:[selected[@"profilePicURL"] stringByReplacingOccurrencesOfString:@"\\/" withString:@"/"]] placeholderImage:[UIImage imageNamed:@"DefaultAccount"]];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
if (indexPath.row == self.accountList.count) {
[self actionAddAccount:cell];
return;
}
self.modalInPresentation = YES;
self.tableView.userInteractionEnabled = NO;
[self addActivityIndicatorTo:cell];
id callback = ^(NSString* status, BOOL success) {
[self callbackMicrosoftAuth:status success:success forCell:cell];
};
[[BaseAuthenticator loadSavedName:self.accountList[indexPath.row][@"username"]] refreshTokenWithCallback:callback];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// TODO: invalidate token
NSString *str = self.accountList[indexPath.row][@"username"];
NSFileManager *fm = [NSFileManager defaultManager];
NSString *path = [NSString stringWithFormat:@"%s/accounts/%@.json", getenv("POJAV_HOME"), str];
if (self.whenDelete != nil) {
self.whenDelete(str);
}
[fm removeItemAtPath:path error:nil];
[self.accountList removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == self.accountList.count) {
return UITableViewCellEditingStyleNone;
} else {
return UITableViewCellEditingStyleDelete;
}
}
- (void)actionAddAccount:(UITableViewCell *)sender {
UIAlertController *picker = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
UIAlertAction *actionMicrosoft = [UIAlertAction actionWithTitle:localize(@"login.option.microsoft", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[self actionLoginMicrosoft:sender];
}];
[picker addAction:actionMicrosoft];
UIAlertAction *actionLocal = [UIAlertAction actionWithTitle:localize(@"login.option.local", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[self actionLoginLocal:sender];
}];
[picker addAction:actionLocal];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:localize(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil];
[picker addAction:cancel];
picker.popoverPresentationController.sourceView = sender;
picker.popoverPresentationController.sourceRect = sender.bounds;
[self presentViewController:picker animated:YES completion:nil];
}
- (void)actionLoginLocal:(UIView *)sender {
if ([getPreference(@"local_warn") boolValue] == YES) {
setPreference(@"local_warn", @NO);
UIAlertController *alert = [UIAlertController alertControllerWithTitle:localize(@"login.warn.title.localmode", nil) message:localize(@"login.warn.message.localmode", nil) preferredStyle:UIAlertControllerStyleActionSheet];
alert.popoverPresentationController.sourceView = sender;
alert.popoverPresentationController.sourceRect = sender.bounds;
UIAlertAction *ok = [UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {[self actionLoginLocal:sender];}];
[alert addAction:ok];
[self presentViewController:alert animated:YES completion:nil];
return;
}
UIAlertController *controller = [UIAlertController alertControllerWithTitle:localize(@"Sign in", nil) message:localize(@"login.option.local", nil) preferredStyle:UIAlertControllerStyleAlert];
[controller addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.placeholder = localize(@"login.alert.field.username", nil);
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
textField.borderStyle = UITextBorderStyleRoundedRect;
}];
[controller addAction:[UIAlertAction actionWithTitle:localize(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
NSArray *textFields = controller.textFields;
UITextField *usernameField = textFields[0];
if (usernameField.text.length < 3 || usernameField.text.length > 16) {
controller.message = localize(@"login.error.username.outOfRange", nil);
[self presentViewController:controller animated:YES completion:nil];
} else {
id callback = ^(NSString* status, BOOL success) {
[self dismissViewControllerAnimated:YES completion:nil];
self.whenItemSelected();
};
[[[LocalAuthenticator alloc] initWithInput:usernameField.text] loginWithCallback:callback];
}
}]];
[controller addAction:[UIAlertAction actionWithTitle:localize(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:controller animated:YES completion:nil];
}
- (void)actionLoginMicrosoft:(UITableViewCell *)sender {
NSURL *url = [NSURL URLWithString:@"https://login.live.com/oauth20_authorize.srf?client_id=00000000402b5328&response_type=code&scope=service%3A%3Auser.auth.xboxlive.com%3A%3AMBI_SSL&redirect_url=https%3A%2F%2Flogin.live.com%2Foauth20_desktop.srf"];
self.authVC =
[[ASWebAuthenticationSession alloc] initWithURL:url
callbackURLScheme:@"ms-xal-00000000402b5328"
completionHandler:^(NSURL * _Nullable callbackURL, NSError * _Nullable error)
{
if (callbackURL == nil) {
if (error.code != ASWebAuthenticationSessionErrorCodeCanceledLogin) {
showDialog(self, localize(@"Error", nil), error.localizedDescription);
}
return;
}
NSString *urlString = [callbackURL absoluteString];
// NSLog(@"URL returned = %@", [callbackURL absoluteString]);
if ([urlString containsString:@"/auth/?code="]) {
self.modalInPresentation = YES;
self.tableView.userInteractionEnabled = NO;
[self addActivityIndicatorTo:sender];
NSArray *components = [urlString componentsSeparatedByString:@"/auth/?code="];
id callback = ^(NSString* status, BOOL success) {
if ([status isEqualToString:@"DEMO"] && success) {
showDialog(self, localize(@"login.warn.title.demomode", nil), localize(@"login.warn.message.demomode", nil));
}
[self callbackMicrosoftAuth:status success:success forCell:sender];
};
[[[MicrosoftAuthenticator alloc] initWithInput:components[1]] loginWithCallback:callback];
} else {
NSArray *components = [urlString componentsSeparatedByString:@"/auth/?error="];
if ([components[1] hasPrefix:@"access_denied"]) {
// Ignore access denial responses
return;
}
NSString *outError = [components[1]
stringByReplacingOccurrencesOfString:@"&error_description=" withString:@": "];
outError = [outError stringByRemovingPercentEncoding];
showDialog(self, localize(@"Error", nil), outError);
}
}];
self.authVC.prefersEphemeralWebBrowserSession = YES;
self.authVC.presentationContextProvider = self;
if ([self.authVC start] == NO) {
showDialog(self, localize(@"Error", nil), @"Unable to open Safari");
}
}
- (void)addActivityIndicatorTo:(UITableViewCell *)cell {
UIActivityIndicatorViewStyle indicatorStyle = UIActivityIndicatorViewStyleMedium;
UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:indicatorStyle];
cell.accessoryView = indicator;
[indicator sizeToFit];
[indicator startAnimating];
}
- (void)removeActivityIndicatorFrom:(UITableViewCell *)cell {
UIActivityIndicatorView *indicator = (id)cell.accessoryView;
[indicator stopAnimating];
cell.accessoryView = nil;
}
- (void)callbackMicrosoftAuth:(NSString *)status success:(BOOL)success forCell:(UITableViewCell *)cell {
if (status != nil) {
cell.detailTextLabel.text = status;
if (!success) {
self.modalInPresentation = NO;
self.tableView.userInteractionEnabled = YES;
[self removeActivityIndicatorFrom:cell];
NSLog(@"[MSA] Error: %@", status);
showDialog(self, localize(@"Error", nil), status);
}
} else if (success) {
[self removeActivityIndicatorFrom:cell];
[self dismissViewControllerAnimated:YES completion:nil];
self.whenItemSelected();
}
}
#pragma mark - UIPopoverPresentationControllerDelegate
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller traitCollection:(UITraitCollection *)traitCollection {
return UIModalPresentationNone;
}
#pragma mark - ASWebAuthenticationPresentationContextProviding
- (ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:(ASWebAuthenticationSession *)session {
return UIApplication.sharedApplication.windows.firstObject;
}
@end