AISleepCoachController.m 13.9 KB
//
//  AISleepCoachController.m
//  DreamSleep
//
//  Created by peter on 2022/4/1.
//

#import "AISleepCoachController.h"
#import <WebKit/WebKit.h>
#import "SomeProxy.h"
#import "DsMaskView.h"
#import "RelaxTrainController.h"
#import "UnityGameController.h"
#import "SleepReadyController.h"

@interface AISleepCoachController () <WKNavigationDelegate, WKScriptMessageHandler, DsWebControllerDelegate, RelaxTrainControllerDelegate>
@property (strong, nonatomic) WKWebView *aiWebView;
@property (strong, nonatomic) NSMutableURLRequest *request;
@property (strong, nonatomic) UIProgressView *progressView;
@property (strong, nonatomic) ExceptionDefaultView *exceptionView;
@property (strong, nonatomic) DsMaskView *dsMaskView;
/// 是否已获取当前webview的title
@property (assign, nonatomic) BOOL webViewLoadTitle;
@end

@implementation AISleepCoachController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.dk_backgroundColorPicker = DKColorPickerWithKey(VCViewBG);
    
    // 创建WKWebView对象,添加到界面(storyboard没有控件)
    [self.view addSubview:self.aiWebView];
    [self.view addSubview:self.progressView];
    
    // 监听AI睡眠教练页面需要刷新数据通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(needUpdateAiCoach) name:NeedUpdateAICoach object:nil];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    [self.dsMaskView display];
    
    /*
     webview已获取到title,但返回时无法获取title:
     可能是内存不足造成的,此时需要重新加载同时避免第一次进入重新加载;
     */
    if (self.webViewLoadTitle == YES && self.aiWebView.title == nil) {
        [self.aiWebView reload];
    }
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    if (self.aiWebView.title) {
        self.webViewLoadTitle = YES;
    }
}

- (void)dealloc {
    [self.aiWebView.configuration.userContentController removeScriptMessageHandlerForName:@"AppModel"];
    [self.aiWebView removeObserver:self forKeyPath:@"estimatedProgress"];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:NeedUpdateAICoach object:nil];
}

- (void)needUpdateAiCoach {
    // 拼接Cookies的方式同步cookie
    NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    NSDictionary *dict = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
    self.request.allHTTPHeaderFields = dict;
    [self.aiWebView loadRequest:self.request];
}

#pragma mark - DsWebControllerDelegate && RelaxTrainControllerDelegate && SleepReadyControllerDelegate
- (void)reloadAIPage {
    [self.aiWebView reload];
}

#pragma mark - WKWebView的监听方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"estimatedProgress"]) {
        self.progressView.progress = self.aiWebView.estimatedProgress;
        if (self.progressView.progress == 1) { self.progressView.hidden = YES; }
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(nonnull WKNavigationAction *)navigationAction decisionHandler:(nonnull void (^)(WKNavigationActionPolicy))decisionHandler {
    NSString *url = navigationAction.request.URL.absoluteString;
    DSLog(@"AI睡眠教练主页面入口->跳转链接:%@", url);
    
    // 为了解决跨域问题,每次跳转url时把cookies拼接上
    NSMutableURLRequest *request = (NSMutableURLRequest *)navigationAction.request;
    NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
    NSDictionary *dict = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
    request.allHTTPHeaderFields = dict;
    
    if ([url isEqualToString:AICoachURL]) {
        decisionHandler(WKNavigationActionPolicyAllow);
    } else if ([url isEqualToString:[NSString stringWithFormat:@"%@/%@", ServerURL, @"sleep/ssmian/sleep_prepare"]]) {
        // 拦截安睡准备
        SleepReadyController *srVC = [SleepReadyController new];
        [self.navigationController pushViewController:srVC animated:YES];
        decisionHandler(WKNavigationActionPolicyCancel);
    } else {
        // 开启新的webview页面加载
        DsWebController *newWebVC = [[DsWebController alloc] initWithLink:url isShowNavi:NO];
        newWebVC.refreshDelegate = self;
        [self.navigationController pushViewController:newWebVC animated:YES];
        decisionHandler(WKNavigationActionPolicyCancel);
    }
}

// 开始加载
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}

// 加载成功
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
    self.exceptionView.hidden = YES;
    self.aiWebView.hidden = !self.exceptionView.hidden;
    
    [self serverErrorDeal];
}

// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    DSLog(@"加载失败:%@", error.userInfo);
    [self wkErrorDeal];
}

// 提交发生错误时调用
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    DSLog(@"提交失败:%@", error.userInfo);
    [self wkErrorDeal];
}

// 内存不足出现白屏问题
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
    [self.aiWebView reload];
    
    // 友盟自定义异常
    NSString *url = self.aiWebView.URL.absoluteString.lastPathComponent ? self.aiWebView.URL.absoluteString.lastPathComponent : @"空链接";
    [DataStatisticsUtil reportExceptionWithName:H5Monitor reason:@"webViewWebContentProcessDidTerminate" stackTrace:@[url]];
}

#pragma mark - 错误处理
- (void)wkErrorDeal {
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
    self.exceptionView.hidden = NO;
    self.aiWebView.hidden = !self.exceptionView.hidden;
}

- (void)serverErrorDeal {
    // 网页加载成功了,处理服务器常见错误
    NSURLSessionTask * datatask = [[NSURLSession sharedSession] dataTaskWithRequest:self.request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        DSLog(@"statusCode:%ld, error:%@, info:%@", httpResponse.statusCode, error, [NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode]);
        
        if (httpResponse.statusCode == 404 || httpResponse.statusCode == 502) {
            dispatch_async(dispatch_get_main_queue(), ^{
                self.exceptionView.hidden = NO;
                self.aiWebView.hidden = !self.exceptionView.hidden;
                [self.exceptionView showServerErrInfo:[NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode]];
            });
        }
    }];
    [datatask resume];
}

#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    if ([message.name isEqualToString:@"AppModel"]) {
        NSDictionary *bodyDic = message.body;
        int type = [bodyDic[@"type"] intValue];
        DSLog(@"bodyDic:%@", bodyDic);
        switch (type) {
            case 1: // 跳到登录页面(服务端检查到session过期或用户退出登录后访问需要登录的页面时会触发,不是立即触发)
            {
                if (![LoginUtils getUserLoginData]) {
                    [LoginUtils jumpToLoginControllerWithTarget:self];
                }
            }
                break;
            case 2: // 登录点击开启-开启我的页面里面的AI睡眠教练
            {
                [UserRequestModel autoLoginRequestWithCompletion:^(UserRequestModel * _Nonnull requestModel) {
                    if (requestModel.resCode == DSResCodeSuccess) {
                        [[NSNotificationCenter defaultCenter] postNotificationName:NeedUpdateStartAI object:nil];
                    } else if (requestModel.resCode == DSResCodeFail) {
                        // 自动登录失败,清除本地用户信息并且跳转到登录页面
                        [LoginUtils clearUserLoginData];
                        [LoginUtils jumpToLoginControllerWithTarget:self];
                    }
                }];
            }
                break;
            case 3: // 轻拍哄睡
            {
                UnityGameController *gameVC = [UnityGameController new];
                gameVC.gameType = GameTypeCoax;
                [self.navigationController pushViewController:gameVC animated:YES];
            }
                break;
            case 4: // 安心记事本
            {
                [DSProgressHUD showToast:@"该功能未来会被替换"];
            }
                break;
            case 5: // 练习腹式呼吸法
            {
                RelaxTrainController *relaxVC = [RelaxTrainController new];
                relaxVC.refreshDelegate = self;
                relaxVC.params = bodyDic;
                [self.navigationController pushViewController:relaxVC animated:YES];
            }
                break;
            case 6: // AI睡眠教练顶部安睡准备(跳转到安睡准备页面)
            {
                SleepReadyController *srVC = [SleepReadyController new];
                [self.navigationController pushViewController:srVC animated:YES];
            }
                break;
            case 7: // 跳转到登录页面(未注册或注销后在AI睡眠教练里点击开启跳转到登录页面)
            {
                if (![LoginUtils getUserLoginData]) {
                    [LoginUtils jumpToLoginControllerWithTarget:self isAccess:YES];
                }
            }
                break;
            case 8: // 每日计划-安睡准备(跳转到安睡准备设置页面)
            {
                SleepReadyController *srVC = [SleepReadyController new];
                [self.navigationController pushViewController:srVC animated:NO];

                ReadyListController *setListVC = [[ReadyListController alloc] initWithDelegate:srVC];
                [self.navigationController pushViewController:setListVC animated:YES];
            }
                break;
            default:
                break;
        }
    }
}

#pragma mark - lazy
- (WKWebView *)aiWebView {
    if (!_aiWebView) {
        // 进行配置控制器
        WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
        // 实例化对象
        configuration.userContentController = [WKUserContentController new];
        // 调用JS方法
        [configuration.userContentController addScriptMessageHandler:[[SomeProxy alloc] initObject:self] name:@"AppModel"];
        
        // 进行偏好设置
        WKPreferences *preferences = [WKPreferences new];
        preferences.javaScriptCanOpenWindowsAutomatically = YES;
        preferences.javaScriptEnabled = YES;
        configuration.preferences = preferences;
        
        // 初始化WKWebView
        _aiWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight - kTabBarHeight) configuration:configuration];
        _aiWebView.backgroundColor = DSWhite;
        _aiWebView.hidden = YES;
        _aiWebView.scrollView.bounces = NO;
        [_aiWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
        _aiWebView.navigationDelegate = self;
        _aiWebView.scrollView.showsVerticalScrollIndicator = NO;
        _aiWebView.scrollView.showsHorizontalScrollIndicator = NO;
        // 解决页面顶部出现白色问题
        _aiWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
        
        self.request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:AICoachURL] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10.0];
        NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
        NSDictionary *dict = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
        self.request.allHTTPHeaderFields = dict;
        [_aiWebView loadRequest:self.request];
    }
    return _aiWebView;
}

- (UIProgressView *)progressView {
    if (!_progressView) {
        _progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 0)];
        _progressView.tintColor = HighlightColor;
        // 通过放大Y轴来改变高度
        _progressView.transform = CGAffineTransformMakeScale(1.0f, 3.0f);;
    }
    return _progressView;
}

- (ExceptionDefaultView *)exceptionView {
    if (!_exceptionView) {
        WS(weakSelf);
        _exceptionView = [[ExceptionDefaultView alloc] initWithType:ExceptionTypeNet block:^{
            weakSelf.progressView.hidden = NO;
            weakSelf.progressView.progress = 0;
            weakSelf.exceptionView.hidden = YES;
            [weakSelf.aiWebView loadRequest:weakSelf.request];
        } superView:self.view];
    }
    return _exceptionView;
}

- (DsMaskView *)dsMaskView {
    if (!_dsMaskView) {
        _dsMaskView = [[DsMaskView alloc] initWithSuperView:self.view];
    }
    return _dsMaskView;
}

#pragma mark - 关闭侧滑
- (BOOL)enableInteractivePopGestureRecognizer {
    return NO;
}

#pragma mark - 隐藏导航栏
- (BOOL)isShowNavigationBar {
    return YES;
}

#pragma mark - 品牌模式
- (NaviStyle)navigationBarStyle {
    return NaviStyleDefault;
}

#pragma mark - 设置状态栏文字颜色
- (UIStatusBarStyle)preferredStatusBarStyle {
    return [self.dk_manager.themeVersion isEqualToString:DKThemeVersionNormal] ? UIStatusBarStyleDefault : UIStatusBarStyleLightContent;
}

@end