com.azerion.bluestack 3.0.3 → 3.1.0-preview2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Editor/Android/AndroidBuildPostProcess.cs +0 -1
- package/Editor/Android/AndroidDependency.cs +1 -2
- package/Editor/Android/AndroidDependencyModifier.cs +55 -0
- package/Editor/Android/AndroidDependencyModifier.cs.meta +3 -0
- package/Editor/Android/AndroidDependencyParser.cs +26 -1
- package/Editor/Android/ManifestProcessor.cs +2 -1
- package/Editor/Azerion.BlueStack.Editor.asmdef +14 -14
- package/Editor/BlueStackDependencies.xml +1 -1
- package/Editor/BlueStackMediationDependencies.xml +11 -0
- package/Editor/BlueStackMediationDependencies.xml.meta +7 -0
- package/Editor/BlueStackSettings.cs +21 -18
- package/Editor/BlueStackSettingsEditor.cs +64 -1
- package/Editor/DebugHelper.cs +2 -2
- package/Editor/Dependency.cs +1 -1
- package/Editor/DependencyProvider.cs +213 -2
- package/Editor/IDependencyModifier.cs +12 -0
- package/Editor/IDependencyModifier.cs.meta +11 -0
- package/Editor/IDependencyParser.cs +1 -0
- package/Editor/MediationNetworkDependency.cs +34 -0
- package/Editor/MediationNetworkDependency.cs.meta +3 -0
- package/Editor/iOS/IOSDependency.cs +12 -0
- package/Editor/iOS/IOSDependency.cs.meta +11 -0
- package/Editor/iOS/IOSDependencyModifier.cs +105 -0
- package/Editor/iOS/IOSDependencyModifier.cs.meta +3 -0
- package/Editor/iOS/IOSDependencyParser.cs +32 -0
- package/Editor/iOS/PodDependency/PodDependencyUtils.cs +5 -1
- package/Example/Azerion.BlueStack.Example.asmdef +14 -14
- package/Example/Scripts/NativeAdManager.cs +5 -7
- package/Example/Scripts/SmallNativeAdManager.cs +4 -6
- package/README.md +4 -1
- package/Runtime/API/AdSize.cs +2 -2
- package/Runtime/API/Banner/BannerAd.cs +1 -1
- package/Runtime/API/BlueStackAds.cs +7 -3
- package/Runtime/API/InterstitialAd.cs +20 -5
- package/Runtime/API/NativeAd/NativeAd.cs +20 -26
- package/Runtime/API/NativeAd/NativeAdEventArgs.cs +1 -5
- package/Runtime/API/NativeAd/NativeAdInteractionHandler.cs +176 -173
- package/Runtime/API/NativeAd/NativeAdLoader.cs +10 -14
- package/Runtime/API/NativeAd/NativeAdObject.cs +563 -547
- package/Runtime/API/NativeAd/NativeAdType.cs +6 -6
- package/Runtime/API/NativeAd/NativeAssetInfo.cs +28 -28
- package/Runtime/Azerion.BlueStack.asmdef +14 -14
- package/Runtime/Common/NativeAd/AdLoaderClientArgs.cs +8 -8
- package/Runtime/Common/NativeAd/AdsEventExecutor.cs +69 -68
- package/Runtime/Common/NativeAd/DummyNativeClient.cs +52 -52
- package/Runtime/Common/NativeAd/NativeClientEventArgs.cs +5 -5
- package/Runtime/Common/NativeAd/NativeUtils.cs +111 -107
- package/Runtime/Internal/IBannerAdClient.cs +1 -1
- package/Runtime/Internal/IBlueStackClient.cs +2 -1
- package/Runtime/Internal/NativeAd/IAdLoaderClient.cs +12 -12
- package/Runtime/Internal/NativeAd/INativeAdClient.cs +1 -1
- package/Runtime/Internal/NativeAd/INativeClientFactory.cs +5 -5
- package/Runtime/Platforms/Android/BlueStackClient.cs +2 -1
- package/Runtime/Platforms/Android/BlueStackNativeClassNames.cs +2 -1
- package/Runtime/Platforms/Unity/AdvertisementManager.cs +5 -4
- package/Runtime/Platforms/Unity/BannerAdClient.cs +16 -9
- package/Runtime/Platforms/Unity/BaseAdClient.cs +7 -5
- package/Runtime/Platforms/Unity/BlueStackClient.cs +4 -3
- package/Runtime/Platforms/Unity/ClientsFactory.cs +0 -1
- package/Runtime/Platforms/Unity/InitializationStatusClient.cs +1 -0
- package/Runtime/Platforms/Unity/InterstitialAdClient.cs +12 -6
- package/Runtime/Platforms/Unity/NativeAdClient.cs +16 -10
- package/Runtime/Platforms/Unity/RewardedVideoAdClient.cs +9 -3
- package/Runtime/Platforms/Unity/Utils.cs +7 -3
- package/Runtime/Platforms/iOS/BannerAdClient.cs +33 -24
- package/Runtime/Platforms/iOS/BlueStackClient.cs +14 -10
- package/Runtime/Platforms/iOS/ClientsFactory.cs +0 -2
- package/Runtime/Platforms/iOS/Externs.cs +72 -65
- package/Runtime/Platforms/iOS/InitializationStatusClient.cs +6 -5
- package/Runtime/Platforms/iOS/InterstitialAdClient.cs +27 -21
- package/Runtime/Platforms/iOS/NativeAdClient.cs +27 -22
- package/Runtime/Platforms/iOS/PreferenceClient.cs +1 -1
- package/Runtime/Platforms/iOS/RewardedVideoAdClient.cs +27 -20
- package/Runtime/Platforms/iOS/Utils.cs +6 -3
- package/Runtime/Utilities/MiniJSON.cs +12 -12
- package/package.json +1 -1
- package/Editor/iOS/Dependency.cs +0 -0
- package/Editor/iOS/Dependency.cs.meta +0 -3
|
@@ -9,550 +9,566 @@ using UnityEngine.UI;
|
|
|
9
9
|
|
|
10
10
|
namespace Azerion.BlueStack.API
|
|
11
11
|
{
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
12
|
+
// Struct to store screen space bounds
|
|
13
|
+
internal struct VisibilityInfo
|
|
14
|
+
{
|
|
15
|
+
public float PercentageOfVisibleArea; // Percentage of visible area
|
|
16
|
+
public float PercentageOfScreenSpace; // Percentage of screen space
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
internal struct ClickInfo
|
|
20
|
+
{
|
|
21
|
+
public Vector3 ClickPosition;
|
|
22
|
+
public float ClickTime;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
internal class ClickEventArgs : EventArgs
|
|
26
|
+
{
|
|
27
|
+
public ClickInfo StartClick { get; set; }
|
|
28
|
+
public ClickInfo EndClick { get; set; }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
internal enum HitStatus
|
|
32
|
+
{
|
|
33
|
+
Miss,
|
|
34
|
+
ObstructedHit,
|
|
35
|
+
UnobstructedHit,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
[AddComponentMenu(""), DisallowMultipleComponent, HideInInspector]
|
|
39
|
+
internal sealed class NativeAdObject : MonoBehaviour
|
|
40
|
+
{
|
|
41
|
+
private const float _timeInterval = 0.1f;
|
|
42
|
+
private const int _iterations = 5;
|
|
43
|
+
private float _lastInteractionTime;
|
|
44
|
+
private ClickInfo _clickInfo;
|
|
45
|
+
private bool _is2DColliderPresent;
|
|
46
|
+
private bool _isInteractionStarted;
|
|
47
|
+
private bool _isRectTransPresent; // check again
|
|
48
|
+
internal event EventHandler<EventArgs> OnAdDisplay;
|
|
49
|
+
internal event EventHandler<ClickEventArgs> OnAdClick;
|
|
50
|
+
internal bool IsImpressionChecked;
|
|
51
|
+
internal bool HasMadeImpression;
|
|
52
|
+
internal bool IsImpressive;
|
|
53
|
+
internal bool IsClickable;
|
|
54
|
+
|
|
55
|
+
internal float InteractionTime { get; private set; }
|
|
56
|
+
|
|
57
|
+
internal string ObjectTag { get; private set; }
|
|
58
|
+
|
|
59
|
+
public void Start()
|
|
60
|
+
{
|
|
61
|
+
this._lastInteractionTime = 0f;
|
|
62
|
+
this.IsImpressionChecked = false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public void Initialize(string nativeAssetID, bool isImpressive = false, bool isClickable = false)
|
|
66
|
+
{
|
|
67
|
+
ObjectTag = nativeAssetID;
|
|
68
|
+
IsImpressive = isImpressive;
|
|
69
|
+
IsClickable = isClickable;
|
|
70
|
+
|
|
71
|
+
if (GetComponent<Collider>() == null)
|
|
72
|
+
{
|
|
73
|
+
if (GetComponent<Collider2D>() == null)
|
|
74
|
+
{
|
|
75
|
+
throw new InvalidOperationException(" No Collider or Collider2D component found.");
|
|
76
|
+
}
|
|
77
|
+
else
|
|
78
|
+
{
|
|
79
|
+
_is2DColliderPresent = true;
|
|
80
|
+
Debug.Log("2D Collider Present!!!");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else
|
|
84
|
+
{
|
|
85
|
+
Debug.Log("Collider Present!!!");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check if the GameObject has a Renderer component.
|
|
89
|
+
Renderer rendererComponent = GetComponent<Renderer>();
|
|
90
|
+
if (rendererComponent != null)
|
|
91
|
+
{
|
|
92
|
+
if (!rendererComponent.isVisible)
|
|
93
|
+
{
|
|
94
|
+
throw new InvalidOperationException(" Object is not visible.");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else if (IsValidUIObject())
|
|
98
|
+
{
|
|
99
|
+
Debug.Log("IsValidUIObject");
|
|
100
|
+
_isRectTransPresent = true;
|
|
101
|
+
}
|
|
102
|
+
else
|
|
103
|
+
{
|
|
104
|
+
throw new InvalidOperationException(" Not a valid Ad Object.");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (IsImpressive)
|
|
108
|
+
InvokeRepeating("CheckForImpression", 0.0f, this.TimeInterval());
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
ClickInfo _startClick;
|
|
112
|
+
ClickInfo _endClick;
|
|
113
|
+
|
|
114
|
+
public void Update()
|
|
115
|
+
{
|
|
116
|
+
if (!IsClickable) return;
|
|
117
|
+
|
|
118
|
+
// track clickable elements
|
|
119
|
+
this._lastInteractionTime += (Time.deltaTime - this._lastInteractionTime) * 0.1f;
|
|
120
|
+
|
|
121
|
+
if (Input.GetMouseButtonDown(0))
|
|
122
|
+
{
|
|
123
|
+
this._isInteractionStarted = true;
|
|
124
|
+
_startClick.ClickPosition = Input.mousePosition;
|
|
125
|
+
_startClick.ClickTime = Time.time * 1000f;
|
|
126
|
+
}
|
|
127
|
+
else if (this._isInteractionStarted && Input.GetMouseButtonUp(0))
|
|
128
|
+
{
|
|
129
|
+
this._isInteractionStarted = false;
|
|
130
|
+
_endClick.ClickPosition = Input.mousePosition;
|
|
131
|
+
_endClick.ClickTime = Time.time * 1000f;
|
|
132
|
+
|
|
133
|
+
HitStatus hitStatus;
|
|
134
|
+
|
|
135
|
+
if (!_is2DColliderPresent)
|
|
136
|
+
{
|
|
137
|
+
hitStatus = CheckHitStatus3D(Input.mousePosition);
|
|
138
|
+
}
|
|
139
|
+
else
|
|
140
|
+
{
|
|
141
|
+
hitStatus = CheckHitStatus2D(Input.mousePosition);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (hitStatus == HitStatus.UnobstructedHit)
|
|
145
|
+
{
|
|
146
|
+
OnAdClick?.Invoke(this, new ClickEventArgs { StartClick = _startClick, EndClick = _endClick });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Method to get screen space bounds
|
|
152
|
+
internal VisibilityInfo GetVisibilityInfo()
|
|
153
|
+
{
|
|
154
|
+
// Get camera component
|
|
155
|
+
Camera mainCamera = NativeUtils.GetCamera();
|
|
156
|
+
VisibilityInfo visibilityInfo = new VisibilityInfo(); // Create a new struct
|
|
157
|
+
// Get gameobject to check bounds for
|
|
158
|
+
GameObject currentGameObject = this.gameObject;
|
|
159
|
+
// Get world space bounds
|
|
160
|
+
Bounds worldSpaceBounds;
|
|
161
|
+
if (!_is2DColliderPresent) // Check if 3D object
|
|
162
|
+
{
|
|
163
|
+
worldSpaceBounds = currentGameObject.GetComponent<Collider>().bounds;
|
|
164
|
+
}
|
|
165
|
+
else
|
|
166
|
+
{
|
|
167
|
+
worldSpaceBounds = currentGameObject.GetComponent<Collider2D>().bounds;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Get max bounds
|
|
171
|
+
Vector3 maxBounds = NativeUtils.GetBoundsMax(currentGameObject, mainCamera, worldSpaceBounds);
|
|
172
|
+
// Get min bounds
|
|
173
|
+
Vector3 minBounds = NativeUtils.GetBoundsMin(currentGameObject, mainCamera, worldSpaceBounds);
|
|
174
|
+
|
|
175
|
+
// Calculate screen space bounds
|
|
176
|
+
float minX = Math.Max(0.0f, minBounds.x);
|
|
177
|
+
float minY = Math.Max(0.0f, minBounds.y);
|
|
178
|
+
float maxX = Math.Min(Screen.width, maxBounds.x);
|
|
179
|
+
float maxY = Math.Min(Screen.height, maxBounds.y);
|
|
180
|
+
|
|
181
|
+
// Increment amounts
|
|
182
|
+
int xDivisions = (int)((maxX - minX) / 5.0);
|
|
183
|
+
int yDivisions = (int)((maxY - minY) / 5.0);
|
|
184
|
+
|
|
185
|
+
// Check if center is visible
|
|
186
|
+
if (xDivisions <= 0 || yDivisions <= 0)
|
|
187
|
+
{
|
|
188
|
+
return visibilityInfo;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Calculate actual area
|
|
192
|
+
float actualWidth = maxBounds.x - minBounds.x;
|
|
193
|
+
float actualHeight = maxBounds.y - minBounds.y;
|
|
194
|
+
|
|
195
|
+
// Calculate screen space area
|
|
196
|
+
float width = maxX - minX;
|
|
197
|
+
float height = maxY - minY;
|
|
198
|
+
|
|
199
|
+
// Check if height, width are neg
|
|
200
|
+
if (width <= 0.0f || height <= 0.0f)
|
|
201
|
+
{
|
|
202
|
+
return visibilityInfo;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
float areaOnScreen = width * height;
|
|
206
|
+
float percentageOfAreaOnScreen = areaOnScreen / (actualWidth * actualHeight);
|
|
207
|
+
|
|
208
|
+
// visibilityInfo.PercentageOfVisibleArea = !this._is2DColliderPresent ? this.CalculatePercentageOfVisibleArea3D(minX, maxX, minY, maxY, xDivisions, yDivisions)
|
|
209
|
+
// : this.CalculatePercentageOfVisibleArea3D(minX, maxX, minY, maxY, xDivisions, yDivisions);
|
|
210
|
+
|
|
211
|
+
// Set values in Visibility Info
|
|
212
|
+
visibilityInfo.PercentageOfVisibleArea = CalculatePercentageOfVisibleArea(minX, maxX, minY, maxY,
|
|
213
|
+
xDivisions, yDivisions, percentageOfAreaOnScreen);
|
|
214
|
+
visibilityInfo.PercentageOfScreenSpace = areaOnScreen / (Screen.width * Screen.height);
|
|
215
|
+
return visibilityInfo;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Methods to check if the given input vector is obstructed or unobstructed
|
|
219
|
+
private HitStatus CheckHitStatus2D(Vector3 position)
|
|
220
|
+
{
|
|
221
|
+
// Create a ray from the camera to the specified screen point
|
|
222
|
+
Ray ray = NativeUtils.GetCamera().ScreenPointToRay(position);
|
|
223
|
+
|
|
224
|
+
// Perform a 2D raycast and store the result
|
|
225
|
+
var rayIntersection = Physics2D.GetRayIntersection(ray);
|
|
226
|
+
// Check if the raycast hit is obstructed by a Collider component
|
|
227
|
+
if ((UnityEngine.Object)rayIntersection.collider != null)
|
|
228
|
+
{
|
|
229
|
+
if (rayIntersection.collider == GetComponent<Collider2D>())
|
|
230
|
+
{
|
|
231
|
+
// Perform a 3D raycast and store the result
|
|
232
|
+
if (Physics.Raycast(ray, out var hitInfo, float.PositiveInfinity))
|
|
233
|
+
{
|
|
234
|
+
// Check if the 3D raycast hit is closer than the 2D raycast hit
|
|
235
|
+
if (rayIntersection.distance >= hitInfo.distance)
|
|
236
|
+
{
|
|
237
|
+
return HitStatus.ObstructedHit;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Return unobstructed hit if no obstructions were found
|
|
242
|
+
return HitStatus.UnobstructedHit;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Perform a 2D raycast and store the results in an array
|
|
247
|
+
RaycastHit2D[] rayIntersectionAll = Physics2D.GetRayIntersectionAll(ray);
|
|
248
|
+
// Loop through the raycast hits
|
|
249
|
+
foreach (var raycastHit2D in rayIntersectionAll)
|
|
250
|
+
{
|
|
251
|
+
// Check if the raycast hit is obstructed by the Collider2D component
|
|
252
|
+
if (raycastHit2D.collider == GetComponent<Collider2D>())
|
|
253
|
+
{
|
|
254
|
+
return HitStatus.ObstructedHit;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return this.CheckHitStatusCanvas(position);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private HitStatus CheckHitStatusCanvas(Vector3 position)
|
|
262
|
+
{
|
|
263
|
+
GraphicRaycaster parentGraphicRaycaster = GetComponentInParent<GraphicRaycaster>();
|
|
264
|
+
if (parentGraphicRaycaster == null)
|
|
265
|
+
{
|
|
266
|
+
return HitStatus.Miss;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
EventSystem currentEventSystem = EventSystem.current;
|
|
270
|
+
if (currentEventSystem == null)
|
|
271
|
+
{
|
|
272
|
+
return HitStatus.Miss;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
PointerEventData pointerEventData = new PointerEventData(currentEventSystem);
|
|
276
|
+
pointerEventData.position = position;
|
|
277
|
+
List<RaycastResult> raycastResults = new List<RaycastResult>();
|
|
278
|
+
parentGraphicRaycaster.Raycast(pointerEventData, raycastResults);
|
|
279
|
+
GameObject hitGameObject = null;
|
|
280
|
+
bool isHit = false;
|
|
281
|
+
int maxDepth = 0;
|
|
282
|
+
foreach (RaycastResult result in raycastResults)
|
|
283
|
+
{
|
|
284
|
+
if (result.gameObject == gameObject)
|
|
285
|
+
{
|
|
286
|
+
isHit = true;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (result.depth > maxDepth)
|
|
290
|
+
{
|
|
291
|
+
maxDepth = result.depth;
|
|
292
|
+
hitGameObject = result.gameObject;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (!isHit)
|
|
297
|
+
{
|
|
298
|
+
return HitStatus.Miss;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (hitGameObject == gameObject)
|
|
302
|
+
{
|
|
303
|
+
return HitStatus.UnobstructedHit;
|
|
304
|
+
}
|
|
305
|
+
else
|
|
306
|
+
{
|
|
307
|
+
return HitStatus.ObstructedHit;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private HitStatus CheckHitStatus3D(Vector3 position)
|
|
312
|
+
{
|
|
313
|
+
// Create a ray from the camera to the input vector
|
|
314
|
+
Ray ray = NativeUtils.GetCamera().ScreenPointToRay(position);
|
|
315
|
+
// Initialize variables for raycast hits
|
|
316
|
+
RaycastHit hitInfo;
|
|
317
|
+
RaycastHit2D rayIntersection;
|
|
318
|
+
// bool isHit = GetComponent<Collider>().Raycast(ray, out hitInfo, float.PositiveInfinity);
|
|
319
|
+
|
|
320
|
+
// Check for 3D raycast hit
|
|
321
|
+
if (Physics.Raycast(ray, out hitInfo, float.PositiveInfinity))
|
|
322
|
+
{
|
|
323
|
+
// Check if the hit object is the current object
|
|
324
|
+
if (hitInfo.collider == GetComponent<Collider>())
|
|
325
|
+
{
|
|
326
|
+
return HitStatus.UnobstructedHit;
|
|
327
|
+
}
|
|
328
|
+
else
|
|
329
|
+
{
|
|
330
|
+
// Check for 2D raycast hit
|
|
331
|
+
rayIntersection = Physics2D.GetRayIntersection(ray);
|
|
332
|
+
if ((UnityEngine.Object)rayIntersection.collider != null &&
|
|
333
|
+
rayIntersection.distance <= hitInfo.distance)
|
|
334
|
+
{
|
|
335
|
+
return HitStatus.UnobstructedHit;
|
|
336
|
+
}
|
|
337
|
+
else
|
|
338
|
+
{
|
|
339
|
+
return HitStatus.ObstructedHit;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return this.CheckHitStatusCanvas(position);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Method to check for Unobstructed Visible Area in screen space
|
|
348
|
+
private float CalculatePercentageOfVisibleArea(float minX, float maxX, float minY, float maxY, int xDivisions,
|
|
349
|
+
int yDivisions, float percentageOfAreaOnScreen)
|
|
350
|
+
{
|
|
351
|
+
// Initialize variables
|
|
352
|
+
float numUnobstructedPoints = 0.0f;
|
|
353
|
+
float totalPoints = 0.0f;
|
|
354
|
+
float currentX = minX;
|
|
355
|
+
float currentY = minY;
|
|
356
|
+
// Increment through screen space
|
|
357
|
+
while (currentX <= maxX)
|
|
358
|
+
{
|
|
359
|
+
while (currentY <= maxY)
|
|
360
|
+
{
|
|
361
|
+
// Check if point is unobstructed
|
|
362
|
+
HitStatus obstructionCheckResult = !_is2DColliderPresent
|
|
363
|
+
? CheckHitStatus3D(new Vector3(currentX, currentY, 0.0f))
|
|
364
|
+
: CheckHitStatus2D(new Vector3(currentX, currentY, 0.0f));
|
|
365
|
+
if (obstructionCheckResult == HitStatus.UnobstructedHit)
|
|
366
|
+
{
|
|
367
|
+
numUnobstructedPoints++;
|
|
368
|
+
totalPoints++;
|
|
369
|
+
}
|
|
370
|
+
else if (obstructionCheckResult == HitStatus.ObstructedHit)
|
|
371
|
+
{
|
|
372
|
+
totalPoints++;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
currentY += yDivisions;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
currentX += xDivisions;
|
|
379
|
+
currentY = minY;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return percentageOfAreaOnScreen * (numUnobstructedPoints / totalPoints);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
public void OnDestroy()
|
|
386
|
+
{
|
|
387
|
+
base.CancelInvoke();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
public void OnBecameVisible()
|
|
391
|
+
{
|
|
392
|
+
if (!this.IsImpressionChecked && IsImpressive)
|
|
393
|
+
{
|
|
394
|
+
InvokeRepeating("CheckForImpression", 1.0f, this.TimeInterval());
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
public void OnBecameInvisible() => CancelInvoke();
|
|
399
|
+
|
|
400
|
+
public void StopImpressionCheck()
|
|
401
|
+
{
|
|
402
|
+
base.CancelInvoke();
|
|
403
|
+
this.IsImpressionChecked = true;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Method to check if a UI element overlaps with the screen
|
|
407
|
+
private bool DoesUIElementOverlapScreen()
|
|
408
|
+
{
|
|
409
|
+
// Get the RectTransform component
|
|
410
|
+
RectTransform rectTransform = this.GetComponent<RectTransform>();
|
|
411
|
+
// Create a rect for the screen size
|
|
412
|
+
Rect screenRect = new Rect(0.0f, 0.0f, Screen.width, Screen.height);
|
|
413
|
+
// Get the anchored position of the UI element
|
|
414
|
+
float x = rectTransform.anchoredPosition.x + rectTransform.position.x;
|
|
415
|
+
float y = Screen.height - rectTransform.position.y - rectTransform.anchoredPosition.y;
|
|
416
|
+
// Get the size of the UI element
|
|
417
|
+
Vector2 elementSize = Vector2.Scale(rectTransform.rect.size, rectTransform.lossyScale);
|
|
418
|
+
// Create a rect for the UI element
|
|
419
|
+
Rect elementRect = new Rect(x, y, elementSize.x, elementSize.y);
|
|
420
|
+
// Check if the UI element rect overlaps with the screen rect
|
|
421
|
+
Debug.LogWarning("Does screenRect.Overlaps: " + screenRect.Overlaps(elementRect, false));
|
|
422
|
+
if (screenRect.Overlaps(elementRect, false))
|
|
423
|
+
{
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Method to check if a gameobject is in the camera's view frustum
|
|
431
|
+
private bool IsInViewFrustum()
|
|
432
|
+
{
|
|
433
|
+
// Check if the gameobject is rendered in the screen space overlay canvas
|
|
434
|
+
if (!NativeUtils.IsRenderedInScreenSpaceOverlayCanvas(gameObject))
|
|
435
|
+
{
|
|
436
|
+
// Get the camera component
|
|
437
|
+
Camera mainCamera = NativeUtils.GetCamera();
|
|
438
|
+
// Get the bounds of the gameobject
|
|
439
|
+
Bounds bounds;
|
|
440
|
+
if (_is2DColliderPresent)
|
|
441
|
+
{
|
|
442
|
+
bounds = this.GetComponent<Collider2D>().bounds;
|
|
443
|
+
}
|
|
444
|
+
else
|
|
445
|
+
{
|
|
446
|
+
bounds = this.GetComponent<Collider>().bounds;
|
|
447
|
+
}
|
|
448
|
+
// Debug.LogWarning("bounds: " + bounds);
|
|
449
|
+
|
|
450
|
+
// Get the view frustum planes
|
|
451
|
+
Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes(mainCamera);
|
|
452
|
+
|
|
453
|
+
// Check if the gameobject bounds intersect with the view frustum planes
|
|
454
|
+
// Debug.LogWarning("TestPlanesAABB: " + GeometryUtility.TestPlanesAABB(frustumPlanes, bounds));
|
|
455
|
+
if (!GeometryUtility.TestPlanesAABB(frustumPlanes, bounds))
|
|
456
|
+
{
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Get the max bounds of the gameobject
|
|
461
|
+
Vector3 maxBounds = NativeUtils.GetBoundsMax(this.gameObject, mainCamera, bounds);
|
|
462
|
+
// Get the min bounds of the gameobject
|
|
463
|
+
Vector3 minBounds = NativeUtils.GetBoundsMin(this.gameObject, mainCamera, bounds);
|
|
464
|
+
|
|
465
|
+
float height = Math.Min(Screen.height, maxBounds.y) - Math.Max(0.0f, minBounds.y);
|
|
466
|
+
float width = Math.Min(Screen.width, maxBounds.x) - Math.Max(0.0f, minBounds.x);
|
|
467
|
+
|
|
468
|
+
// Debug.LogWarning("height: " + height);
|
|
469
|
+
// Debug.LogWarning("width: " + width);
|
|
470
|
+
|
|
471
|
+
// Check if the gameobject bounds are too small to be visible
|
|
472
|
+
if (height <= 0.01f * Screen.height || width <= 0.01f * Screen.width)
|
|
473
|
+
{
|
|
474
|
+
return false;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
else // When rendered in the screen space overlay canvas
|
|
478
|
+
{
|
|
479
|
+
Debug.LogWarning("DoesUIElementOverlapScreen: " + DoesUIElementOverlapScreen());
|
|
480
|
+
|
|
481
|
+
// Check if the UI element overlaps with the screen
|
|
482
|
+
if (!DoesUIElementOverlapScreen())
|
|
483
|
+
{
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return true;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
private void CheckForImpression()
|
|
492
|
+
{
|
|
493
|
+
VisibilityInfo visibilityInfo = GetVisibilityInfo();
|
|
494
|
+
if (!IsImpressionChecked && IsInViewFrustum())
|
|
495
|
+
{
|
|
496
|
+
if (visibilityInfo.PercentageOfVisibleArea >= 0.5f) // if Visible Area is >= 50%
|
|
497
|
+
{
|
|
498
|
+
HasMadeImpression = true;
|
|
499
|
+
OnAdDisplay?.Invoke(this, EventArgs.Empty);
|
|
500
|
+
}
|
|
501
|
+
else
|
|
502
|
+
{
|
|
503
|
+
HasMadeImpression = false;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
private float TimeInterval()
|
|
509
|
+
{
|
|
510
|
+
return 1.0f;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// TODO: More testing and Improvement required
|
|
514
|
+
// TODO: BSSDK-401 : Move viewability to container level - may not have a container
|
|
515
|
+
private bool IsValidUIObject()
|
|
516
|
+
{
|
|
517
|
+
Canvas canvas = GetComponentInParent<Canvas>();
|
|
518
|
+
if (canvas == null)
|
|
519
|
+
{
|
|
520
|
+
Debug.Log("Unable to find Canvas component in parents");
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
RectTransform rectTransform = GetComponent<RectTransform>();
|
|
525
|
+
if (rectTransform == null)
|
|
526
|
+
{
|
|
527
|
+
Debug.Log("RectTransform component is null");
|
|
528
|
+
return false;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
CanvasRenderer canvasRenderer = GetComponent<CanvasRenderer>();
|
|
532
|
+
if (canvasRenderer == null)
|
|
533
|
+
{
|
|
534
|
+
Debug.Log("CanvasRenderer component is null");
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Rect rect = rectTransform.rect;
|
|
539
|
+
//
|
|
540
|
+
// // Get the center position of the RectTransform in its local coordinates
|
|
541
|
+
// Vector2 centerLocalPosition = new Vector2(rect.center.x, rect.center.y);
|
|
542
|
+
//
|
|
543
|
+
// // Convert the local center position to world position
|
|
544
|
+
// Vector3 centerWorldPosition = rectTransform.TransformPoint(centerLocalPosition);
|
|
545
|
+
// Debug.Log("centerWorldPosition: " + centerWorldPosition);
|
|
546
|
+
//
|
|
547
|
+
// Vector3 viewportPoint = NativeUtils.GetCamera().WorldToViewportPoint(centerWorldPosition);
|
|
548
|
+
// Debug.Log("viewportPoints: " + viewportPoint);
|
|
549
|
+
// if (viewportPoint.x >= 0 && viewportPoint.x <= 1 && viewportPoint.y >= 0 && viewportPoint.y <= 1 && viewportPoint.z > 0)
|
|
550
|
+
// {
|
|
551
|
+
// return true;
|
|
552
|
+
// }
|
|
553
|
+
|
|
554
|
+
// Check if it is completely inside the viewport
|
|
555
|
+
// Vector3[] corners = new Vector3[4];
|
|
556
|
+
// rectTransform.GetWorldCorners(corners);
|
|
557
|
+
// Debug.Log("Ad Object: " + this.name + " - " + corners.Length);
|
|
558
|
+
// foreach (Vector3 corner in corners)
|
|
559
|
+
// {
|
|
560
|
+
// // Debug.Log("corner: " + corner);
|
|
561
|
+
// Vector3 viewportPoint = Camera.main.WorldToViewportPoint(corner);
|
|
562
|
+
// // Debug.Log("viewportPoints: " + viewportPoint);
|
|
563
|
+
// if (viewportPoint.x >= 0 && viewportPoint.x <= 1 && viewportPoint.y >= 0 && viewportPoint.y <= 1 && viewportPoint.z > 0)
|
|
564
|
+
// {
|
|
565
|
+
// //return true;
|
|
566
|
+
// }
|
|
567
|
+
// }
|
|
568
|
+
|
|
569
|
+
// Debug.Log("Not a Valid UI Object!");
|
|
570
|
+
|
|
571
|
+
return true;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|