Files
filza27.2/WQStartupUI.m
T

273 lines
12 KiB
Objective-C

//
// WQStartupUI.m
// FilzaSlop
//
// Blocking launch panel: the exploit runs and every supported directory is
// indexed before Filza's browser becomes interactive. This prevents the
// empty-folder flash that occurs when Filza restores its own home path first.
//
#import "WQStartupUI.h"
#import "WQFilzaIntegration.h"
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
static UIWindow *gStartupWindow;
static UIWindow *gPreviousKeyWindow;
static BOOL gStartupBegan;
@interface WQStartupViewController : UIViewController
@property (nonatomic, strong) UIProgressView *progressView;
@property (nonatomic, strong) UILabel *statusLabel;
@property (nonatomic, strong) UILabel *percentLabel;
@property (nonatomic, strong) UILabel *detailLabel;
@property (nonatomic, strong) UIActivityIndicatorView *spinner;
@property (nonatomic, strong) NSArray<id> *observers;
@property (nonatomic, assign) BOOL finished;
@property (nonatomic, assign) NSTimeInterval shownAt;
@property (nonatomic, assign) double lastProgress;
@end
@implementation WQStartupViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithWhite:0.035 alpha:1.0];
self.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;
self.shownAt = [NSDate timeIntervalSinceReferenceDate];
UIView *card = [UIView new];
card.translatesAutoresizingMaskIntoConstraints = NO;
card.backgroundColor = [UIColor colorWithWhite:0.105 alpha:1.0];
card.layer.cornerRadius = 22;
card.layer.cornerCurve = kCACornerCurveContinuous;
[self.view addSubview:card];
UIImage *icon = [UIImage imageNamed:@"AppIcon60x60"];
if (!icon) icon = [UIImage imageNamed:@"AppIcon"];
UIImageView *iconView = [[UIImageView alloc] initWithImage:icon];
iconView.translatesAutoresizingMaskIntoConstraints = NO;
iconView.layer.cornerRadius = 16;
iconView.layer.cornerCurve = kCACornerCurveContinuous;
iconView.clipsToBounds = YES;
iconView.contentMode = UIViewContentModeScaleAspectFill;
[card addSubview:iconView];
UILabel *title = [UILabel new];
title.translatesAutoresizingMaskIntoConstraints = NO;
title.text = @"Filza WQ";
title.textColor = UIColor.whiteColor;
title.font = [UIFont systemFontOfSize:25 weight:UIFontWeightBold];
title.textAlignment = NSTextAlignmentCenter;
[card addSubview:title];
UILabel *subtitle = [UILabel new];
subtitle.translatesAutoresizingMaskIntoConstraints = NO;
subtitle.text = @"iOS 27.2 · worse_query sandbox access";
subtitle.textColor = [UIColor colorWithWhite:0.62 alpha:1.0];
subtitle.font = [UIFont systemFontOfSize:12 weight:UIFontWeightMedium];
subtitle.textAlignment = NSTextAlignmentCenter;
[card addSubview:subtitle];
self.statusLabel = [UILabel new];
self.statusLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.statusLabel.text = @"正在启动…";
self.statusLabel.textColor = UIColor.whiteColor;
self.statusLabel.font = [UIFont systemFontOfSize:15 weight:UIFontWeightSemibold];
self.statusLabel.textAlignment = NSTextAlignmentCenter;
self.statusLabel.numberOfLines = 2;
[card addSubview:self.statusLabel];
self.progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
self.progressView.translatesAutoresizingMaskIntoConstraints = NO;
self.progressView.progress = 0.02;
self.progressView.progressTintColor = [UIColor colorWithRed:0.20 green:0.55 blue:1.0 alpha:1.0];
self.progressView.trackTintColor = [UIColor colorWithWhite:0.24 alpha:1.0];
self.progressView.layer.cornerRadius = 3;
self.progressView.clipsToBounds = YES;
[card addSubview:self.progressView];
self.percentLabel = [UILabel new];
self.percentLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.percentLabel.text = @"2%";
self.percentLabel.textColor = [UIColor colorWithWhite:0.72 alpha:1.0];
self.percentLabel.font = [UIFont monospacedDigitSystemFontOfSize:12 weight:UIFontWeightMedium];
self.percentLabel.textAlignment = NSTextAlignmentCenter;
[card addSubview:self.percentLabel];
self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleMedium];
self.spinner.translatesAutoresizingMaskIntoConstraints = NO;
self.spinner.color = [UIColor colorWithWhite:0.72 alpha:1.0];
[self.spinner startAnimating];
[card addSubview:self.spinner];
self.detailLabel = [UILabel new];
self.detailLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.detailLabel.text = @"应用数据、App Group、系统组与扩展目录加载完成后才会进入。";
self.detailLabel.textColor = [UIColor colorWithWhite:0.52 alpha:1.0];
self.detailLabel.font = [UIFont systemFontOfSize:11];
self.detailLabel.textAlignment = NSTextAlignmentCenter;
self.detailLabel.numberOfLines = 3;
[card addSubview:self.detailLabel];
CGFloat cardWidth = MIN(340, self.view.bounds.size.width - 48);
[NSLayoutConstraint activateConstraints:@[
[card.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
[card.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
[card.widthAnchor constraintEqualToConstant:cardWidth],
[iconView.topAnchor constraintEqualToAnchor:card.topAnchor constant:26],
[iconView.centerXAnchor constraintEqualToAnchor:card.centerXAnchor],
[iconView.widthAnchor constraintEqualToConstant:72],
[iconView.heightAnchor constraintEqualToConstant:72],
[title.topAnchor constraintEqualToAnchor:iconView.bottomAnchor constant:12],
[title.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:20],
[title.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-20],
[subtitle.topAnchor constraintEqualToAnchor:title.bottomAnchor constant:3],
[subtitle.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:20],
[subtitle.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-20],
[self.statusLabel.topAnchor constraintEqualToAnchor:subtitle.bottomAnchor constant:22],
[self.statusLabel.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:20],
[self.statusLabel.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-20],
[self.progressView.topAnchor constraintEqualToAnchor:self.statusLabel.bottomAnchor constant:14],
[self.progressView.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:24],
[self.progressView.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-24],
[self.progressView.heightAnchor constraintEqualToConstant:5],
[self.percentLabel.topAnchor constraintEqualToAnchor:self.progressView.bottomAnchor constant:6],
[self.percentLabel.centerXAnchor constraintEqualToAnchor:card.centerXAnchor],
[self.spinner.topAnchor constraintEqualToAnchor:self.percentLabel.bottomAnchor constant:12],
[self.spinner.centerXAnchor constraintEqualToAnchor:card.centerXAnchor],
[self.detailLabel.topAnchor constraintEqualToAnchor:self.spinner.bottomAnchor constant:10],
[self.detailLabel.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:20],
[self.detailLabel.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-20],
[self.detailLabel.bottomAnchor constraintEqualToAnchor:card.bottomAnchor constant:-22],
]];
__weak typeof(self) weakSelf = self;
NSNotificationCenter *center = NSNotificationCenter.defaultCenter;
id progressObserver = [center addObserverForName:@"WQStartupProgress"
object:nil queue:NSOperationQueue.mainQueue
usingBlock:^(NSNotification *notification) {
[weakSelf applyProgress:notification.userInfo];
}];
id finishObserver = [center addObserverForName:@"WQStartupFinished"
object:nil queue:NSOperationQueue.mainQueue
usingBlock:^(NSNotification *notification) {
[weakSelf finish];
}];
self.observers = @[progressObserver, finishObserver];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 12 * NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
if (weakSelf.finished) return;
if (weakSelf.lastProgress < 0.9)
weakSelf.detailLabel.text = @"目录较多时首次索引可能需要几十秒,请保持应用在前台。";
});
}
- (void)applyProgress:(NSDictionary *)info
{
NSNumber *progressValue = info[@"progress"];
NSString *status = info[@"status"];
double progress = progressValue.doubleValue;
if (progress > self.lastProgress) self.lastProgress = progress;
if (self.lastProgress > 1.0) self.lastProgress = 1.0;
if (status.length) self.statusLabel.text = status;
[self.progressView setProgress:(float)MAX(self.lastProgress, 0.02) animated:YES];
self.percentLabel.text = [NSString stringWithFormat:@"%d%%",
(int)round(self.lastProgress * 100.0)];
if (progress >= 1.0) {
[self.spinner stopAnimating];
self.statusLabel.text = @"正在进入 Filza…";
}
}
- (void)finish
{
if (self.finished) return;
self.finished = YES;
[self applyProgress:@{ @"progress": @1.0, @"status": @"准备完成,正在进入 Filza…" }];
[self.spinner stopAnimating];
NSTimeInterval elapsed = [NSDate timeIntervalSinceReferenceDate] - self.shownAt;
NSTimeInterval delay = MAX(0.35, 1.1 - elapsed);
__weak typeof(self) weakSelf = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
UIWindow *overlay = gStartupWindow;
[UIView animateWithDuration:0.28 animations:^{
overlay.alpha = 0.0;
} completion:^(BOOL completed) {
overlay.hidden = YES;
gStartupWindow = nil;
[UIApplication.sharedApplication setIdleTimerDisabled:NO];
if (gPreviousKeyWindow) {
[gPreviousKeyWindow makeKeyWindow];
} else {
for (UIWindow *candidate in UIApplication.sharedApplication.windows) {
if (candidate != overlay && !candidate.hidden) {
[candidate makeKeyWindow];
break;
}
}
}
gPreviousKeyWindow = nil;
[NSNotificationCenter.defaultCenter
postNotificationName:@"WQStartupUIDidDismiss" object:nil];
(void)weakSelf;
}];
});
}
- (void)dealloc
{
for (id observer in self.observers)
[NSNotificationCenter.defaultCenter removeObserver:observer];
}
@end
void WQStartupUIBegin(void)
{
if (gStartupBegan) return;
gStartupBegan = YES;
dispatch_async(dispatch_get_main_queue(), ^{
if (gStartupWindow) return;
gPreviousKeyWindow = UIApplication.sharedApplication.keyWindow;
gStartupWindow = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
gStartupWindow.windowLevel = UIWindowLevelAlert + 10;
gStartupWindow.backgroundColor = UIColor.blackColor;
NSSet<UIScene *> *scenes = UIApplication.sharedApplication.connectedScenes;
for (UIScene *scene in scenes) {
if ([scene isKindOfClass:UIWindowScene.class]) {
gStartupWindow.windowScene = (UIWindowScene *)scene;
break;
}
}
WQStartupViewController *controller = [WQStartupViewController new];
// Force viewDidLoad so the progress notifications cannot race the
// first report from WQFilzaStart.
(void)controller.view;
gStartupWindow.rootViewController = controller;
gStartupWindow.alpha = 1.0;
[gStartupWindow makeKeyAndVisible];
[UIApplication.sharedApplication setIdleTimerDisabled:YES];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 120 * NSEC_PER_MSEC),
dispatch_get_main_queue(), ^{
WQFilzaStart();
});
});
}