Skip to content

Commit 47d148f

Browse files
add: tolerance value to screen render capturing
1 parent 6227629 commit 47d148f

File tree

10 files changed

+120
-53
lines changed

10 files changed

+120
-53
lines changed

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ android {
5252
}
5353

5454
dependencies {
55-
api 'com.instabug.library:instabug:15.0.2.7020723-SNAPSHOT'
55+
api 'com.instabug.library:instabug:15.0.2.7046628-SNAPSHOT'
5656
testImplementation 'junit:junit:4.13.2'
5757
testImplementation "org.mockito:mockito-inline:3.12.1"
5858
testImplementation "io.mockk:mockk:1.13.13"

android/src/main/java/com/instabug/flutter/modules/ApmApi.java

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
import com.instabug.apm.InternalAPM;
1010
import com.instabug.apm.configuration.cp.APMFeature;
1111
import com.instabug.apm.configuration.cp.FeatureAvailabilityCallback;
12-
import com.instabug.apm.model.ExecutionTrace;
12+
import com.instabug.apm.configuration.cp.ToleranceValueCallback;
13+
//import com.instabug.apm.model.ExecutionTrace;
1314
import com.instabug.apm.networking.APMNetworkLogger;
1415
import com.instabug.apm.networkinterception.cp.APMCPNetworkLog;
1516
import com.instabug.apm.screenrendering.models.cp.IBGFrameData;
@@ -31,11 +32,11 @@
3132

3233
public class ApmApi implements ApmPigeon.ApmHostApi {
3334
private final String TAG = ApmApi.class.getName();
34-
private final HashMap<String, ExecutionTrace> traces = new HashMap<>();
35-
private final Callable<Float> refreshRate;
35+
// private final HashMap<String, ExecutionTrace> traces = new HashMap<>();
36+
private final Callable<Float> refreshRateCallback;
3637

3738
public ApmApi(Callable<Float> refreshRate) {
38-
this.refreshRate = refreshRate;
39+
this.refreshRateCallback = refreshRate;
3940
}
4041

4142
public static void init(BinaryMessenger messenger, Callable<Float> refreshRateProvider) {
@@ -114,24 +115,24 @@ public void startExecutionTrace(@NonNull String id, @NonNull String name, ApmPig
114115
@Override
115116
public void run() {
116117
try {
117-
ExecutionTrace trace = APM.startExecutionTrace(name);
118-
if (trace != null) {
119-
traces.put(id, trace);
120-
121-
ThreadManager.runOnMainThread(new Runnable() {
122-
@Override
123-
public void run() {
124-
result.success(id);
125-
}
126-
});
127-
} else {
128-
ThreadManager.runOnMainThread(new Runnable() {
129-
@Override
130-
public void run() {
131-
result.success(null);
132-
}
133-
});
134-
}
118+
// ExecutionTrace trace = APM.startExecutionTrace(name);
119+
// if (trace != null) {
120+
// traces.put(id, trace);
121+
//
122+
// ThreadManager.runOnMainThread(new Runnable() {
123+
// @Override
124+
// public void run() {
125+
// result.success(id);
126+
// }
127+
// });
128+
// } else {
129+
// ThreadManager.runOnMainThread(new Runnable() {
130+
// @Override
131+
// public void run() {
132+
// result.success(null);
133+
// }
134+
// });
135+
// }
135136
} catch (Exception e) {
136137
e.printStackTrace();
137138

@@ -220,7 +221,7 @@ public void endFlow(@NonNull String name) {
220221
@Override
221222
public void setExecutionTraceAttribute(@NonNull String id, @NonNull String key, @NonNull String value) {
222223
try {
223-
traces.get(id).setAttribute(key, value);
224+
// traces.get(id).setAttribute(key, value);
224225
} catch (Exception e) {
225226
e.printStackTrace();
226227
}
@@ -235,7 +236,7 @@ public void setExecutionTraceAttribute(@NonNull String id, @NonNull String key,
235236
@Override
236237
public void endExecutionTrace(@NonNull String id) {
237238
try {
238-
traces.get(id).end();
239+
// traces.get(id).end();
239240
} catch (Exception e) {
240241
e.printStackTrace();
241242
}
@@ -504,9 +505,16 @@ public void invoke(boolean isEnabled) {
504505
}
505506

506507
@Override
507-
public void deviceRefreshRate(@NonNull ApmPigeon.Result<Double> result) {
508+
public void getDeviceRefreshRateAndTolerance(@NonNull ApmPigeon.Result<List<Double>> result) {
508509
try {
509-
result.success(refreshRate.call().doubleValue());
510+
final double refreshRate = refreshRateCallback.call().doubleValue();
511+
InternalAPM._getToleranceValueForScreenRenderingCP(new ToleranceValueCallback() {
512+
@Override
513+
public void invoke(long tolerance) {
514+
result.success(java.util.Arrays.asList(refreshRate, (double) tolerance));
515+
}
516+
});
517+
510518
} catch (Exception e) {
511519
e.printStackTrace();
512520
}

android/src/main/java/com/instabug/flutter/modules/BugReportingApi.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ public void setCommentMinimumCharacterCount(@NonNull Long limit, @Nullable List<
184184
reportTypesArray[i] = ArgsRegistry.reportTypes.get(key);
185185
}
186186
}
187-
BugReporting.setCommentMinimumCharacterCount(limit.intValue(), reportTypesArray);
187+
// BugReporting.setCommentMinimumCharacterCount(limit.intValue(), reportTypesArray);
188188
}
189189

190190
@Override

android/src/main/java/com/instabug/flutter/modules/InstabugApi.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ public void setWelcomeMessageMode(@NonNull String mode) {
176176

177177
@Override
178178
public void setPrimaryColor(@NonNull Long color) {
179-
Instabug.setPrimaryColor(color.intValue());
179+
180180
}
181181

182182
@Override

ios/Classes/Modules/ApmApi.m

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -208,15 +208,6 @@ - (void)setScreenRenderEnabledIsEnabled:(nonnull NSNumber *)isEnabled error:(Flu
208208

209209
}
210210

211-
- (void)deviceRefreshRateWithCompletion:(void (^)(NSNumber * _Nullable, FlutterError * _Nullable))completion{
212-
if (@available(iOS 10.3, *)) {
213-
double refreshRate = [UIScreen mainScreen].maximumFramesPerSecond;
214-
completion(@(refreshRate) ,nil);
215-
} else {
216-
// Fallback for very old iOS versions.
217-
completion(@(60.0) , nil);
218-
}
219-
}
220211

221212
- (void)endScreenRenderForAutoUiTraceData:(nonnull NSDictionary<NSString *,id> *)data error:(FlutterError * _Nullable __autoreleasing * _Nonnull)error {
222213
NSArray<NSArray<NSNumber *> *> *rawFrames = data[@"frameData"];
@@ -254,6 +245,18 @@ - (void)endScreenRenderForCustomUiTraceData:(nonnull NSDictionary<NSString *,id>
254245
[IBGAPM endCustomUITraceCPWithFrames:frameInfos];
255246
}
256247

248+
- (void)getDeviceRefreshRateAndToleranceWithCompletion:(nonnull void (^)(NSArray<NSNumber *> * _Nullable, FlutterError * _Nullable))completion {
249+
if (@available(iOS 10.3, *)) {
250+
double refreshRate = [UIScreen mainScreen].maximumFramesPerSecond;
251+
double tolerance = 10;
252+
completion(@[@(refreshRate), @(tolerance)] ,nil);
253+
} else {
254+
// Fallback for very old iOS versions.
255+
completion(@[@(60.0), @(10.0)] , nil);
256+
}
257+
}
258+
259+
257260

258261

259262
@end

lib/src/modules/apm.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,9 +378,13 @@ class APM {
378378
///
379379
/// Returns:
380380
/// A Future<double> that represent the refresh rate.
381+
/// Retrieve the device refresh rate and tolerance value from native side.
382+
///
383+
/// Returns:
384+
/// A Future<List<double>> where the first element is the refresh rate and the second is the tolerance value.
381385
@internal
382-
static Future<double> getDeviceRefreshRate() {
383-
return _host.deviceRefreshRate();
386+
static Future<List<double?>> getDeviceRefreshRateAndTolerance() {
387+
return _host.getDeviceRefreshRateAndTolerance();
384388
}
385389

386390
/// Sets the screen Render state based on the provided boolean value.

lib/src/utils/screen_rendering/instabug_screen_render_manager.dart

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,6 @@ class InstabugScreenRenderManager {
3434

3535
final List<InstabugFrameData> _delayedFrames = [];
3636

37-
/// 1 / DeviceRefreshRate * 1000
38-
double _deviceRefreshRate = 60;
39-
4037
/// Default refresh rate for 60 FPS displays in milliseconds (16.67ms)
4138
double _slowFrameThresholdMs = 16.67;
4239

@@ -230,8 +227,8 @@ class InstabugScreenRenderManager {
230227
1 / displayRefreshRate * 1000;
231228

232229
/// Get device refresh rate from native side.
233-
Future<double> get _getDeviceRefreshRateFromNative =>
234-
APM.getDeviceRefreshRate();
230+
Future<List<double?>> get _getDeviceRefreshRateAndToleranceFromNative =>
231+
APM.getDeviceRefreshRateAndTolerance();
235232

236233
/// add new [WidgetsBindingObserver] to track app lifecycle.
237234
void _addWidgetBindingObserver() {
@@ -265,12 +262,26 @@ class InstabugScreenRenderManager {
265262
analyzeFrameTiming(frameTiming);
266263
}
267264
};
268-
_deviceRefreshRate = await _getDeviceRefreshRateFromNative;
269-
_slowFrameThresholdMs = _targetMsPerFrame(_deviceRefreshRate);
265+
_slowFrameThresholdMs = await _getSlowFrameThresholdMs;
270266
_screenRenderForAutoUiTrace = InstabugScreenRenderData(frameData: []);
271267
_screenRenderForCustomUiTrace = InstabugScreenRenderData(frameData: []);
272268
}
273269

270+
Future<double> get _getSlowFrameThresholdMs async {
271+
final deviceRefreshRateAndTolerance =
272+
await _getDeviceRefreshRateAndToleranceFromNative;
273+
final deviceRefreshRate = deviceRefreshRateAndTolerance[0] ??
274+
60; // default to 60 FPS if not available
275+
final toleranceMs = (deviceRefreshRateAndTolerance[1] ?? 10) /
276+
1000; // convert the tolerance from microseconds to milliseconds
277+
final targetMsPerFrame = _targetMsPerFrame(deviceRefreshRate);
278+
return double.parse(
279+
(targetMsPerFrame + toleranceMs).toStringAsFixed(
280+
2,
281+
),
282+
); // round the result to the nearest 2 precision digits
283+
}
284+
274285
int _getEpochOffset(FrameTiming firstPatchedFrameTiming) {
275286
return DateTime.now().microsecondsSinceEpoch -
276287
firstPatchedFrameTiming.timestampInMicroseconds(FramePhase.vsyncStart);

pigeons/apm.api.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ abstract class ApmHostApi {
5858
bool isScreenRenderEnabled();
5959

6060
@async
61-
double deviceRefreshRate();
61+
List<double> getDeviceRefreshRateAndTolerance();
6262

6363
void setScreenRenderEnabled(bool isEnabled);
6464

test/apm_test.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -275,10 +275,13 @@ void main() {
275275
verify(mHost.isScreenRenderEnabled());
276276
});
277277

278-
test("[getDeviceRefreshRate] should call host method", () async {
279-
when(mHost.deviceRefreshRate()).thenAnswer((_) async => 60.0);
280-
await APM.getDeviceRefreshRate();
281-
verify(mHost.deviceRefreshRate()).called(1);
278+
test("[getDeviceRefreshRateAndTolerance] should call host method",
279+
() async {
280+
when(mHost.getDeviceRefreshRateAndTolerance()).thenAnswer(
281+
(_) async => [60.0, 10.0],
282+
);
283+
await APM.getDeviceRefreshRateAndTolerance();
284+
verify(mHost.getDeviceRefreshRateAndTolerance()).called(1);
282285
});
283286

284287
test("[setScreenRenderEnabled] should call host method", () async {

test/utils/screen_render/instabug_screen_render_manager_test.dart

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1+
import 'package:flutter/scheduler.dart' show FrameTiming;
2+
import 'package:flutter/widgets.dart';
13
import 'package:flutter_test/flutter_test.dart';
24
import 'package:instabug_flutter/instabug_flutter.dart';
5+
import 'package:instabug_flutter/src/generated/apm.api.g.dart';
36
import 'package:instabug_flutter/src/models/instabug_frame_data.dart';
47
import 'package:instabug_flutter/src/models/instabug_screen_render_data.dart';
58
import 'package:instabug_flutter/src/utils/screen_rendering/instabug_screen_render_manager.dart';
9+
import 'package:mockito/annotations.dart';
610
import 'package:mockito/mockito.dart';
711

812
import 'instabug_screen_render_manager_test.mocks.dart';
913

14+
@GenerateMocks([ApmHostApi, FrameTiming, WidgetsBinding])
1015
void main() {
1116
TestWidgetsFlutterBinding.ensureInitialized();
1217

@@ -19,7 +24,8 @@ void main() {
1924
mWidgetBinding = MockWidgetsBinding();
2025
manager = InstabugScreenRenderManager.init(); // test-only constructor
2126
APM.$setHostApi(mApmHost);
22-
when(mApmHost.deviceRefreshRate()).thenAnswer((_) async => 60);
27+
when(mApmHost.getDeviceRefreshRateAndTolerance())
28+
.thenAnswer((_) async => [60, 0]);
2329
manager.init(mWidgetBinding);
2430
});
2531

@@ -44,6 +50,38 @@ void main() {
4450
});
4551
});
4652

53+
// group('_initStaticValues', () {
54+
//
55+
// test('should initialize _timingsCallback', () async {
56+
// await manager.callInitStaticValues();
57+
// expect(manager.timingsCallback, isNotNull);
58+
// });
59+
//
60+
// test('should initialize _slowFrameThresholdMs with value from _getSlowFrameThresholdMs', () async {
61+
// // Patch the getter to return a known value
62+
// manager.slowFrameThresholdMs = 0.0;
63+
// manager.getSlowFrameThresholdMsFuture = () async => 42.0;
64+
// await manager.callInitStaticValues();
65+
// expect(manager.slowFrameThresholdMs, 42.0);
66+
// });
67+
//
68+
// test('should initialize _screenRenderForAutoUiTrace and _screenRenderForCustomUiTrace as empty InstabugScreenRenderData', () async {
69+
// await manager.callInitStaticValues();
70+
// expect(manager.screenRenderForAutoUiTrace, isA<InstabugScreenRenderData>());
71+
// expect(manager.screenRenderForAutoUiTrace.frameData, isEmpty);
72+
// expect(manager.screenRenderForCustomUiTrace, isA<InstabugScreenRenderData>());
73+
// expect(manager.screenRenderForCustomUiTrace.frameData, isEmpty);
74+
// });
75+
//
76+
// test('should set _epochOffset on first timing in _timingsCallback', () async {
77+
// await manager.callInitStaticValues();
78+
// final mockFrameTiming = MockFrameTiming();
79+
// manager.epochOffset = null;
80+
// manager.timingsCallback([mockFrameTiming]);
81+
// expect(manager.epochOffset, isNotNull);
82+
// });
83+
// });
84+
4785
group('startScreenRenderCollectorForTraceId()', () {
4886
test('should not attach timing listener if it is attached', () async {
4987
manager.startScreenRenderCollectorForTraceId(1);

0 commit comments

Comments
 (0)