xref: /aosp_15_r20/external/walt/ios/WALT/TapLatencyController.m (revision bf47c6829f95be9dd55f4c5bbc44a71c90aad403)
1*bf47c682SAndroid Build Coastguard Worker/*
2*bf47c682SAndroid Build Coastguard Worker * Copyright (C) 2016 The Android Open Source Project
3*bf47c682SAndroid Build Coastguard Worker *
4*bf47c682SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*bf47c682SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*bf47c682SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*bf47c682SAndroid Build Coastguard Worker *
8*bf47c682SAndroid Build Coastguard Worker *      http://www.apache.org/licenses/LICENSE-2.0
9*bf47c682SAndroid Build Coastguard Worker *
10*bf47c682SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*bf47c682SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*bf47c682SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*bf47c682SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*bf47c682SAndroid Build Coastguard Worker * limitations under the License.
15*bf47c682SAndroid Build Coastguard Worker */
16*bf47c682SAndroid Build Coastguard Worker
17*bf47c682SAndroid Build Coastguard Worker#import "TapLatencyController.h"
18*bf47c682SAndroid Build Coastguard Worker
19*bf47c682SAndroid Build Coastguard Worker#import "NSArray+Extensions.h"
20*bf47c682SAndroid Build Coastguard Worker#import "UIAlertView+Extensions.h"
21*bf47c682SAndroid Build Coastguard Worker#import "WALTAppDelegate.h"
22*bf47c682SAndroid Build Coastguard Worker#import "WALTClient.h"
23*bf47c682SAndroid Build Coastguard Worker#import "WALTLogger.h"
24*bf47c682SAndroid Build Coastguard Worker#import "WALTTouch.h"
25*bf47c682SAndroid Build Coastguard Worker
26*bf47c682SAndroid Build Coastguard Worker@interface TapLatencyController ()
27*bf47c682SAndroid Build Coastguard Worker- (void)updateCountDisplay;
28*bf47c682SAndroid Build Coastguard Worker- (void)processEvent:(UIEvent *)event;
29*bf47c682SAndroid Build Coastguard Worker- (void)appendToLogView:(NSString *)string;
30*bf47c682SAndroid Build Coastguard Worker- (void)computeStatisticsForPhase:(UITouchPhase)phase;
31*bf47c682SAndroid Build Coastguard Worker@end
32*bf47c682SAndroid Build Coastguard Worker
33*bf47c682SAndroid Build Coastguard Worker@implementation TapLatencyController {
34*bf47c682SAndroid Build Coastguard Worker  WALTClient *_client;
35*bf47c682SAndroid Build Coastguard Worker  WALTLogger *_logger;
36*bf47c682SAndroid Build Coastguard Worker
37*bf47c682SAndroid Build Coastguard Worker  // Statistics
38*bf47c682SAndroid Build Coastguard Worker  unsigned int _downCount;
39*bf47c682SAndroid Build Coastguard Worker  unsigned int _downCountRecorded;
40*bf47c682SAndroid Build Coastguard Worker  unsigned int _upCount;
41*bf47c682SAndroid Build Coastguard Worker  unsigned int _upCountRecorded;
42*bf47c682SAndroid Build Coastguard Worker
43*bf47c682SAndroid Build Coastguard Worker  NSMutableArray<WALTTouch *> *_touches;
44*bf47c682SAndroid Build Coastguard Worker}
45*bf47c682SAndroid Build Coastguard Worker
46*bf47c682SAndroid Build Coastguard Worker- (void)viewDidLoad {
47*bf47c682SAndroid Build Coastguard Worker  [super viewDidLoad];
48*bf47c682SAndroid Build Coastguard Worker
49*bf47c682SAndroid Build Coastguard Worker  self.logView.selectable = YES;
50*bf47c682SAndroid Build Coastguard Worker  self.logView.text = [NSString string];
51*bf47c682SAndroid Build Coastguard Worker  self.logView.selectable = NO;
52*bf47c682SAndroid Build Coastguard Worker
53*bf47c682SAndroid Build Coastguard Worker  _logger = [WALTLogger sessionLogger];
54*bf47c682SAndroid Build Coastguard Worker  _client = ((WALTAppDelegate *)[UIApplication sharedApplication].delegate).client;
55*bf47c682SAndroid Build Coastguard Worker}
56*bf47c682SAndroid Build Coastguard Worker
57*bf47c682SAndroid Build Coastguard Worker- (void)viewWillAppear:(BOOL)animated {
58*bf47c682SAndroid Build Coastguard Worker  [super viewWillAppear:animated];
59*bf47c682SAndroid Build Coastguard Worker
60*bf47c682SAndroid Build Coastguard Worker  [_logger appendString:@"TAPLATENCY\n"];
61*bf47c682SAndroid Build Coastguard Worker  [self reset:nil];
62*bf47c682SAndroid Build Coastguard Worker}
63*bf47c682SAndroid Build Coastguard Worker
64*bf47c682SAndroid Build Coastguard Worker- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
65*bf47c682SAndroid Build Coastguard Worker  [self processEvent:event];
66*bf47c682SAndroid Build Coastguard Worker  [self updateCountDisplay];
67*bf47c682SAndroid Build Coastguard Worker}
68*bf47c682SAndroid Build Coastguard Worker
69*bf47c682SAndroid Build Coastguard Worker- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
70*bf47c682SAndroid Build Coastguard Worker  [self processEvent:event];
71*bf47c682SAndroid Build Coastguard Worker  [self updateCountDisplay];
72*bf47c682SAndroid Build Coastguard Worker}
73*bf47c682SAndroid Build Coastguard Worker
74*bf47c682SAndroid Build Coastguard Worker- (void)updateCountDisplay {
75*bf47c682SAndroid Build Coastguard Worker  NSString *counts = [NSString stringWithFormat:@"N ↓%u (%u) ↑%u (%u)",
76*bf47c682SAndroid Build Coastguard Worker                      _downCountRecorded, _downCount, _upCountRecorded, _upCount];
77*bf47c682SAndroid Build Coastguard Worker  self.countLabel.text = counts;
78*bf47c682SAndroid Build Coastguard Worker}
79*bf47c682SAndroid Build Coastguard Worker
80*bf47c682SAndroid Build Coastguard Worker- (void)processEvent:(UIEvent *)event {
81*bf47c682SAndroid Build Coastguard Worker  // TODO(pquinn): Pick first/last coalesced touch?
82*bf47c682SAndroid Build Coastguard Worker
83*bf47c682SAndroid Build Coastguard Worker  NSTimeInterval kernelTime = event.timestamp;
84*bf47c682SAndroid Build Coastguard Worker  NSTimeInterval callbackTime = _client.currentTime;
85*bf47c682SAndroid Build Coastguard Worker
86*bf47c682SAndroid Build Coastguard Worker  NSError *error = nil;
87*bf47c682SAndroid Build Coastguard Worker  NSTimeInterval physicalTime = [_client lastShockTimeWithError:&error];
88*bf47c682SAndroid Build Coastguard Worker  if (physicalTime == -1) {
89*bf47c682SAndroid Build Coastguard Worker    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"WALT Connection Error" error:error];
90*bf47c682SAndroid Build Coastguard Worker    [alert show];
91*bf47c682SAndroid Build Coastguard Worker    return;
92*bf47c682SAndroid Build Coastguard Worker  }
93*bf47c682SAndroid Build Coastguard Worker
94*bf47c682SAndroid Build Coastguard Worker  WALTTouch *touch = [[WALTTouch alloc] initWithEvent:event];
95*bf47c682SAndroid Build Coastguard Worker  touch.callbackTime = callbackTime;
96*bf47c682SAndroid Build Coastguard Worker  touch.physicalTime = physicalTime;
97*bf47c682SAndroid Build Coastguard Worker
98*bf47c682SAndroid Build Coastguard Worker  NSString *actionString = nil;
99*bf47c682SAndroid Build Coastguard Worker  if (touch.phase == UITouchPhaseBegan) {
100*bf47c682SAndroid Build Coastguard Worker    _downCount += 1;
101*bf47c682SAndroid Build Coastguard Worker    actionString = @"ACTION_DOWN";
102*bf47c682SAndroid Build Coastguard Worker  } else {
103*bf47c682SAndroid Build Coastguard Worker    _upCount += 1;
104*bf47c682SAndroid Build Coastguard Worker    actionString = @"ACTION_UP";
105*bf47c682SAndroid Build Coastguard Worker  }
106*bf47c682SAndroid Build Coastguard Worker
107*bf47c682SAndroid Build Coastguard Worker  if (physicalTime == 0) {
108*bf47c682SAndroid Build Coastguard Worker    [_logger appendFormat:@"%@\tX\tno shock\n", actionString];
109*bf47c682SAndroid Build Coastguard Worker    [self appendToLogView:[NSString stringWithFormat:@"%@: No shock detected\n", actionString]];
110*bf47c682SAndroid Build Coastguard Worker    return;
111*bf47c682SAndroid Build Coastguard Worker  }
112*bf47c682SAndroid Build Coastguard Worker
113*bf47c682SAndroid Build Coastguard Worker  NSTimeInterval physicalToKernel = kernelTime - physicalTime;
114*bf47c682SAndroid Build Coastguard Worker  NSTimeInterval kernelToCallback = callbackTime - kernelTime;
115*bf47c682SAndroid Build Coastguard Worker
116*bf47c682SAndroid Build Coastguard Worker  if (physicalToKernel < 0 || physicalToKernel > 0.2) {
117*bf47c682SAndroid Build Coastguard Worker    [_logger appendFormat:@"%@\tX\tbogus kernelTime\t%f\n", actionString, physicalToKernel];
118*bf47c682SAndroid Build Coastguard Worker    [self appendToLogView:
119*bf47c682SAndroid Build Coastguard Worker        [NSString stringWithFormat:@"%@: Bogus P → K: %.3f s\n", actionString, physicalToKernel]];
120*bf47c682SAndroid Build Coastguard Worker    return;
121*bf47c682SAndroid Build Coastguard Worker  }
122*bf47c682SAndroid Build Coastguard Worker
123*bf47c682SAndroid Build Coastguard Worker  [_logger appendFormat:@"%@\tO\t%f\t%f\t%f\n",
124*bf47c682SAndroid Build Coastguard Worker      actionString, _client.baseTime, physicalToKernel, kernelToCallback];
125*bf47c682SAndroid Build Coastguard Worker
126*bf47c682SAndroid Build Coastguard Worker  [self appendToLogView:
127*bf47c682SAndroid Build Coastguard Worker      [NSString stringWithFormat:@"%@: P → K: %.3f s; K → C: %.3f s\n",
128*bf47c682SAndroid Build Coastguard Worker        actionString, physicalToKernel, kernelToCallback]];
129*bf47c682SAndroid Build Coastguard Worker
130*bf47c682SAndroid Build Coastguard Worker  [_touches addObject:touch];
131*bf47c682SAndroid Build Coastguard Worker  if (touch.phase == UITouchPhaseBegan) {
132*bf47c682SAndroid Build Coastguard Worker    _downCountRecorded += 1;
133*bf47c682SAndroid Build Coastguard Worker  } else {
134*bf47c682SAndroid Build Coastguard Worker    _upCountRecorded += 1;
135*bf47c682SAndroid Build Coastguard Worker  }
136*bf47c682SAndroid Build Coastguard Worker}
137*bf47c682SAndroid Build Coastguard Worker
138*bf47c682SAndroid Build Coastguard Worker- (IBAction)reset:(id)sender {
139*bf47c682SAndroid Build Coastguard Worker  _downCount = 0;
140*bf47c682SAndroid Build Coastguard Worker  _downCountRecorded = 0;
141*bf47c682SAndroid Build Coastguard Worker  _upCount = 0;
142*bf47c682SAndroid Build Coastguard Worker  _upCountRecorded = 0;
143*bf47c682SAndroid Build Coastguard Worker  [self updateCountDisplay];
144*bf47c682SAndroid Build Coastguard Worker
145*bf47c682SAndroid Build Coastguard Worker  _touches = [[NSMutableArray<WALTTouch *> alloc] init];
146*bf47c682SAndroid Build Coastguard Worker
147*bf47c682SAndroid Build Coastguard Worker  NSError *error = nil;
148*bf47c682SAndroid Build Coastguard Worker  if (![_client syncClocksWithError:&error]) {
149*bf47c682SAndroid Build Coastguard Worker    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"WALT Connection Error" error:error];
150*bf47c682SAndroid Build Coastguard Worker    [alert show];
151*bf47c682SAndroid Build Coastguard Worker  }
152*bf47c682SAndroid Build Coastguard Worker
153*bf47c682SAndroid Build Coastguard Worker  [_logger appendString:@"RESET\n"];
154*bf47c682SAndroid Build Coastguard Worker  [self appendToLogView:@"===========================================\n"];
155*bf47c682SAndroid Build Coastguard Worker}
156*bf47c682SAndroid Build Coastguard Worker
157*bf47c682SAndroid Build Coastguard Worker- (IBAction)computeStatistics:(id)sender {
158*bf47c682SAndroid Build Coastguard Worker  [self appendToLogView:@"-------------------------------------------\n"];
159*bf47c682SAndroid Build Coastguard Worker  [self appendToLogView:@"Medians:\n"];
160*bf47c682SAndroid Build Coastguard Worker  [self computeStatisticsForPhase:UITouchPhaseBegan];
161*bf47c682SAndroid Build Coastguard Worker  [self computeStatisticsForPhase:UITouchPhaseEnded];
162*bf47c682SAndroid Build Coastguard Worker
163*bf47c682SAndroid Build Coastguard Worker  [self reset:sender];
164*bf47c682SAndroid Build Coastguard Worker}
165*bf47c682SAndroid Build Coastguard Worker
166*bf47c682SAndroid Build Coastguard Worker- (void)computeStatisticsForPhase:(UITouchPhase)phase {
167*bf47c682SAndroid Build Coastguard Worker  NSMutableArray<NSNumber *> *p2k = [[NSMutableArray<NSNumber *> alloc] init];
168*bf47c682SAndroid Build Coastguard Worker  NSMutableArray<NSNumber *> *k2c = [[NSMutableArray<NSNumber *> alloc] init];
169*bf47c682SAndroid Build Coastguard Worker
170*bf47c682SAndroid Build Coastguard Worker  for (WALTTouch *touch in _touches) {
171*bf47c682SAndroid Build Coastguard Worker    if (touch.phase != phase) {
172*bf47c682SAndroid Build Coastguard Worker      continue;
173*bf47c682SAndroid Build Coastguard Worker    }
174*bf47c682SAndroid Build Coastguard Worker
175*bf47c682SAndroid Build Coastguard Worker    [p2k addObject:[NSNumber numberWithDouble:touch.kernelTime - touch.physicalTime]];
176*bf47c682SAndroid Build Coastguard Worker    [k2c addObject:[NSNumber numberWithDouble:touch.callbackTime - touch.kernelTime]];
177*bf47c682SAndroid Build Coastguard Worker  }
178*bf47c682SAndroid Build Coastguard Worker
179*bf47c682SAndroid Build Coastguard Worker  NSNumber *p2kMedian = [p2k medianValue];
180*bf47c682SAndroid Build Coastguard Worker  NSNumber *k2cMedian = [k2c medianValue];
181*bf47c682SAndroid Build Coastguard Worker
182*bf47c682SAndroid Build Coastguard Worker  NSString *actionString = (phase == UITouchPhaseBegan ? @"ACTION_DOWN" : @"ACTION_UP");
183*bf47c682SAndroid Build Coastguard Worker  [self appendToLogView:
184*bf47c682SAndroid Build Coastguard Worker      [NSString stringWithFormat:@"%@: P → K: %.3f s; K → C: %.3f s\n",
185*bf47c682SAndroid Build Coastguard Worker        actionString, p2kMedian.doubleValue, k2cMedian.doubleValue]];
186*bf47c682SAndroid Build Coastguard Worker}
187*bf47c682SAndroid Build Coastguard Worker
188*bf47c682SAndroid Build Coastguard Worker- (void)appendToLogView:(NSString*)string {
189*bf47c682SAndroid Build Coastguard Worker  self.logView.selectable = YES;
190*bf47c682SAndroid Build Coastguard Worker  self.logView.text = [self.logView.text stringByAppendingString:string];
191*bf47c682SAndroid Build Coastguard Worker  [self.logView scrollRangeToVisible:NSMakeRange(self.logView.text.length - 2, 1)];
192*bf47c682SAndroid Build Coastguard Worker  self.logView.selectable = NO;
193*bf47c682SAndroid Build Coastguard Worker}
194*bf47c682SAndroid Build Coastguard Worker@end
195