@byteplus/react-native-live-pull 1.1.2-rc.1 → 1.1.3-rc.0
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/android/src/main/java/com/volcengine/velive/rn/pull/pictureInpicture/FloatingWindowService.java +719 -40
- package/lib/commonjs/index.js +19 -37
- package/lib/module/index.js +19 -37
- package/lib/typescript/codegen/ios/api.d.ts +1 -0
- package/lib/typescript/codegen/pack/keytype.d.ts +0 -12
- package/lib/typescript/core/api.d.ts +13 -0
- package/lib/typescript/core/keytype.d.ts +16 -0
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.volcengine.velive.rn.pull.pictureInpicture;
|
|
2
2
|
|
|
3
|
+
import android.animation.ValueAnimator;
|
|
3
4
|
import android.app.Service;
|
|
4
5
|
import android.content.BroadcastReceiver;
|
|
5
6
|
import android.content.Context;
|
|
@@ -24,14 +25,19 @@ import androidx.annotation.Nullable;
|
|
|
24
25
|
import com.volcengine.velive.rn.pull.R;
|
|
25
26
|
|
|
26
27
|
public class FloatingWindowService extends Service {
|
|
28
|
+
private static final int MARGIN_TOP = 40;
|
|
29
|
+
private static final int MARGIN_BOTTOM = 40;
|
|
30
|
+
private static final int MARGIN_LEFT = 20;
|
|
31
|
+
private static final int MARGIN_RIGHT = 20;
|
|
27
32
|
private static final String TAG = FloatingWindowService.class.getSimpleName();
|
|
28
|
-
private static final int LONGER_SIDE_MAX_LEN = 1000;
|
|
29
33
|
|
|
30
34
|
private WindowManager mWindowManager;
|
|
31
35
|
private WindowManager.LayoutParams mLayoutParams;
|
|
32
36
|
private SurfaceView mSurfaceView;
|
|
33
37
|
private View mSmallWindowView;
|
|
34
38
|
private ActivityLaunchReceiver mActivityLaunchReceiver;
|
|
39
|
+
private FloatingOnTouchListener
|
|
40
|
+
mFloatingOnTouchListener; // Store the listener instance
|
|
35
41
|
|
|
36
42
|
public static final String ACTION_STOP_PIP_SERVICE =
|
|
37
43
|
"com.volcengine.velive.rn.pull.STOP_PIP_SERVICE";
|
|
@@ -97,37 +103,68 @@ public class FloatingWindowService extends Service {
|
|
|
97
103
|
int screenWidth = displayMetrics.widthPixels;
|
|
98
104
|
int screenHeight = displayMetrics.heightPixels;
|
|
99
105
|
|
|
100
|
-
|
|
101
|
-
int
|
|
106
|
+
int drawableWidth = screenWidth - MARGIN_LEFT - MARGIN_RIGHT;
|
|
107
|
+
int drawableHeight = screenHeight - MARGIN_TOP - MARGIN_BOTTOM;
|
|
102
108
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
int maxLen
|
|
109
|
+
boolean isPortrait = screenHeight > screenWidth;
|
|
110
|
+
|
|
111
|
+
int maxLen;
|
|
112
|
+
if (isPortrait) {
|
|
113
|
+
// 竖屏时,宽度不能超过 drawableWidth
|
|
114
|
+
maxLen = drawableWidth;
|
|
115
|
+
} else {
|
|
116
|
+
// 横屏时,高度不能超过 drawableHeight
|
|
117
|
+
maxLen = drawableHeight;
|
|
118
|
+
}
|
|
106
119
|
|
|
107
|
-
// Limit the floating window size to prevent it from being too large or too
|
|
108
|
-
// small, control the longer side to maxLen, scale the shorter
|
|
109
|
-
// side proportionally
|
|
110
120
|
int width, height;
|
|
111
|
-
if (aspectRatio >= 1) {
|
|
112
|
-
height = (int)(maxLen / aspectRatio);
|
|
121
|
+
if (aspectRatio >= 1) { // 宽大于等于高 (横向视频或方形)
|
|
113
122
|
width = maxLen;
|
|
114
|
-
|
|
115
|
-
width
|
|
123
|
+
height = (int)(width / aspectRatio);
|
|
124
|
+
if (isPortrait && width > drawableWidth) {
|
|
125
|
+
width = drawableWidth;
|
|
126
|
+
height = (int)(width / aspectRatio);
|
|
127
|
+
}
|
|
128
|
+
if (!isPortrait &&
|
|
129
|
+
height > drawableHeight) { // 横屏下,若计算出的高度超出drawableHeight
|
|
130
|
+
height = drawableHeight;
|
|
131
|
+
width = (int)(height * aspectRatio);
|
|
132
|
+
}
|
|
133
|
+
} else { // 高大于宽 (竖向视频)
|
|
116
134
|
height = maxLen;
|
|
135
|
+
width = (int)(height * aspectRatio);
|
|
136
|
+
if (!isPortrait && height > drawableHeight) {
|
|
137
|
+
height = drawableHeight;
|
|
138
|
+
width = (int)(height * aspectRatio);
|
|
139
|
+
}
|
|
140
|
+
if (isPortrait &&
|
|
141
|
+
width > drawableWidth) { // 竖屏下,若计算出的宽度超出drawableWidth
|
|
142
|
+
width = drawableWidth;
|
|
143
|
+
height = (int)(width / aspectRatio);
|
|
144
|
+
}
|
|
117
145
|
}
|
|
118
|
-
mLayoutParams.width = width;
|
|
119
|
-
mLayoutParams.height = height;
|
|
120
146
|
|
|
121
|
-
//
|
|
122
|
-
mLayoutParams.
|
|
123
|
-
mLayoutParams.
|
|
147
|
+
// Ensure dimensions are positive
|
|
148
|
+
mLayoutParams.width = Math.max(1, width);
|
|
149
|
+
mLayoutParams.height = Math.max(1, height);
|
|
150
|
+
|
|
151
|
+
// Initial position of the floating window, respecting margins
|
|
152
|
+
mLayoutParams.x =
|
|
153
|
+
Math.max(MARGIN_LEFT,
|
|
154
|
+
Math.min(x, screenWidth - mLayoutParams.width - MARGIN_RIGHT));
|
|
155
|
+
mLayoutParams.y =
|
|
156
|
+
Math.max(MARGIN_TOP, Math.min(y, screenHeight - mLayoutParams.height -
|
|
157
|
+
MARGIN_BOTTOM));
|
|
124
158
|
|
|
125
159
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
126
160
|
if (Settings.canDrawOverlays(this)) {
|
|
127
161
|
LayoutInflater layoutInflater = LayoutInflater.from(this);
|
|
128
162
|
mSmallWindowView =
|
|
129
163
|
layoutInflater.inflate(R.layout.floating_window_layout, null);
|
|
130
|
-
|
|
164
|
+
this.mFloatingOnTouchListener =
|
|
165
|
+
new FloatingOnTouchListener(); // Create and store the instance
|
|
166
|
+
mSmallWindowView.setOnTouchListener(
|
|
167
|
+
this.mFloatingOnTouchListener); // Set the stored instance
|
|
131
168
|
mWindowManager.addView(mSmallWindowView, mLayoutParams);
|
|
132
169
|
mSurfaceView = mSmallWindowView.findViewById(R.id.surface_view);
|
|
133
170
|
mSmallWindowView.findViewById(R.id.surface_close_btn)
|
|
@@ -204,6 +241,8 @@ public class FloatingWindowService extends Service {
|
|
|
204
241
|
}
|
|
205
242
|
|
|
206
243
|
private class FloatingOnTouchListener implements View.OnTouchListener {
|
|
244
|
+
private boolean isDoubleClickAnimating =
|
|
245
|
+
false; // Flag to indicate if double-click animation is running
|
|
207
246
|
private final int touchSlop =
|
|
208
247
|
ViewConfiguration
|
|
209
248
|
.get(FloatingWindowService.this.getApplicationContext())
|
|
@@ -212,36 +251,289 @@ public class FloatingWindowService extends Service {
|
|
|
212
251
|
private int x, y;
|
|
213
252
|
private boolean isDragging;
|
|
214
253
|
|
|
254
|
+
private static final int MODE_NONE = 0;
|
|
255
|
+
private static final int MODE_DRAG = 1;
|
|
256
|
+
private static final int MODE_ZOOM = 2;
|
|
257
|
+
private int mode = MODE_NONE;
|
|
258
|
+
|
|
259
|
+
private float oldDist = 1f;
|
|
260
|
+
private float initialAspectRatio;
|
|
261
|
+
private int initialWidth;
|
|
262
|
+
private int initialHeight;
|
|
263
|
+
|
|
264
|
+
// Screen metrics
|
|
265
|
+
private DisplayMetrics displayMetrics = new DisplayMetrics();
|
|
266
|
+
private int screenWidth;
|
|
267
|
+
private int screenHeight;
|
|
268
|
+
private int drawableWidth;
|
|
269
|
+
private int drawableHeight;
|
|
270
|
+
private boolean isPortraitScreen;
|
|
271
|
+
|
|
272
|
+
private long lastClickTime = 0;
|
|
273
|
+
private static final long DOUBLE_CLICK_TIME_DELTA = 200; // milliseconds
|
|
274
|
+
|
|
275
|
+
private final float ZOOM_SCALE_FACTOR =
|
|
276
|
+
1.5f; // Factor to zoom in/out on double tap
|
|
277
|
+
|
|
278
|
+
private void updateScreenMetrics() {
|
|
279
|
+
mWindowManager.getDefaultDisplay().getMetrics(displayMetrics);
|
|
280
|
+
screenWidth = displayMetrics.widthPixels;
|
|
281
|
+
screenHeight = displayMetrics.heightPixels;
|
|
282
|
+
drawableWidth = screenWidth - MARGIN_LEFT - MARGIN_RIGHT;
|
|
283
|
+
drawableHeight = screenHeight - MARGIN_TOP - MARGIN_BOTTOM;
|
|
284
|
+
isPortraitScreen = screenHeight > screenWidth;
|
|
285
|
+
}
|
|
286
|
+
|
|
215
287
|
@Override
|
|
216
288
|
public boolean onTouch(View view, MotionEvent event) {
|
|
217
|
-
|
|
289
|
+
// Update screen metrics at the beginning of a touch gesture,
|
|
290
|
+
// as orientation or screen size might have changed since last touch.
|
|
291
|
+
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
|
292
|
+
updateScreenMetrics();
|
|
293
|
+
}
|
|
294
|
+
// If currently animating from double click, consume touch events to
|
|
295
|
+
// prevent interference
|
|
296
|
+
if (isDoubleClickAnimating) {
|
|
297
|
+
// Allow ACTION_UP to potentially clear the flag if animation ends early
|
|
298
|
+
// or is cancelled but primarily, prevent new gestures from starting.
|
|
299
|
+
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
|
300
|
+
mode = MODE_NONE; // Prevent drag/zoom from starting
|
|
301
|
+
}
|
|
302
|
+
return true; // Consume the event
|
|
303
|
+
}
|
|
304
|
+
switch (event.getAction() & MotionEvent.ACTION_MASK) {
|
|
305
|
+
case MotionEvent.ACTION_DOWN:
|
|
306
|
+
downX = x = (int)event.getRawX();
|
|
307
|
+
downY = y = (int)event.getRawY();
|
|
308
|
+
mode = MODE_DRAG;
|
|
309
|
+
initialAspectRatio = (float)mLayoutParams.width / mLayoutParams.height;
|
|
310
|
+
initialWidth = mLayoutParams.width;
|
|
311
|
+
initialHeight = mLayoutParams.height;
|
|
312
|
+
|
|
313
|
+
// Double tap detection
|
|
314
|
+
long clickTime = System.currentTimeMillis();
|
|
315
|
+
if (clickTime - lastClickTime < DOUBLE_CLICK_TIME_DELTA) {
|
|
316
|
+
handleDoubleClick(view);
|
|
317
|
+
}
|
|
318
|
+
lastClickTime = clickTime;
|
|
319
|
+
break;
|
|
320
|
+
case MotionEvent.ACTION_POINTER_DOWN:
|
|
321
|
+
oldDist = spacing(event);
|
|
322
|
+
if (oldDist > 10f) {
|
|
323
|
+
mode = MODE_ZOOM;
|
|
324
|
+
}
|
|
325
|
+
break;
|
|
218
326
|
case MotionEvent.ACTION_UP:
|
|
327
|
+
case MotionEvent.ACTION_POINTER_UP:
|
|
328
|
+
boolean wasZooming = (mode == MODE_ZOOM);
|
|
329
|
+
// Capture params before mode is reset
|
|
330
|
+
int xBeforeSnap = mLayoutParams.x;
|
|
331
|
+
int widthBeforeSnap = mLayoutParams.width;
|
|
332
|
+
|
|
333
|
+
mode = MODE_NONE; // Reset mode
|
|
334
|
+
|
|
219
335
|
if (isDragging) {
|
|
220
336
|
isDragging = false;
|
|
337
|
+
// Perform snap animation if it was a drag or end of zoom
|
|
338
|
+
if (event.getPointerCount() == 1 || wasZooming) {
|
|
339
|
+
Log.i(TAG, "ACTION_UP/POINTER_UP: Triggering snap. xBeforeSnap=" +
|
|
340
|
+
xBeforeSnap + ", widthBeforeSnap=" +
|
|
341
|
+
widthBeforeSnap + ", wasZooming=" + wasZooming +
|
|
342
|
+
", pointerCount=" + event.getPointerCount());
|
|
343
|
+
animateSnapToEdge(view);
|
|
344
|
+
}
|
|
221
345
|
} else {
|
|
222
|
-
|
|
223
|
-
|
|
346
|
+
// Only trigger click if not zooming and it's a single pointer up
|
|
347
|
+
if (event.getPointerCount() == 1 &&
|
|
348
|
+
(event.getAction() & MotionEvent.ACTION_MASK) ==
|
|
349
|
+
MotionEvent.ACTION_UP) {
|
|
350
|
+
// Check if it was a drag or a tap
|
|
351
|
+
if (Math.abs(event.getRawX() - downX) < touchSlop &&
|
|
352
|
+
Math.abs(event.getRawY() - downY) < touchSlop) {
|
|
353
|
+
// This is a tap, not a double tap, so handle as single click if
|
|
354
|
+
// needed For now, double tap handles zoom, single tap is handled
|
|
355
|
+
// by new_window_btn
|
|
356
|
+
}
|
|
357
|
+
}
|
|
224
358
|
}
|
|
225
|
-
downX
|
|
226
|
-
break;
|
|
227
|
-
case MotionEvent.ACTION_DOWN:
|
|
228
|
-
downX = x = (int)event.getRawX();
|
|
229
|
-
downY = y = (int)event.getRawY();
|
|
359
|
+
// downX and downY are reset at ACTION_DOWN for the primary pointer
|
|
230
360
|
break;
|
|
361
|
+
|
|
231
362
|
case MotionEvent.ACTION_MOVE:
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
363
|
+
if (mode == MODE_DRAG && event.getPointerCount() == 1) {
|
|
364
|
+
int nowX = (int)event.getRawX();
|
|
365
|
+
int nowY = (int)event.getRawY();
|
|
366
|
+
if (!isDragging && (Math.abs(nowX - downX) > touchSlop ||
|
|
367
|
+
Math.abs(nowY - downY) > touchSlop)) {
|
|
368
|
+
isDragging = true;
|
|
369
|
+
}
|
|
370
|
+
if (isDragging) {
|
|
371
|
+
int movedX = nowX - x;
|
|
372
|
+
int movedY = nowY - y;
|
|
373
|
+
mLayoutParams.x = mLayoutParams.x + movedX;
|
|
374
|
+
mLayoutParams.y = mLayoutParams.y + movedY;
|
|
375
|
+
|
|
376
|
+
// 使用预先获取的屏幕高度 screenHeight
|
|
377
|
+
// 限制y坐标在安全区域内
|
|
378
|
+
mLayoutParams.y = Math.max(
|
|
379
|
+
MARGIN_TOP, Math.min(mLayoutParams.y, this.screenHeight -
|
|
380
|
+
mLayoutParams.height -
|
|
381
|
+
MARGIN_BOTTOM));
|
|
382
|
+
|
|
383
|
+
mWindowManager.updateViewLayout(view, mLayoutParams);
|
|
384
|
+
}
|
|
385
|
+
x = nowX;
|
|
386
|
+
y = nowY;
|
|
387
|
+
} else if (mode == MODE_ZOOM && event.getPointerCount() >= 2) {
|
|
388
|
+
float newDist = spacing(event);
|
|
389
|
+
if (newDist > 10f) {
|
|
390
|
+
// Calculate midpoint of the two fingers
|
|
391
|
+
float midX = (event.getX(0) + event.getX(1)) / 2;
|
|
392
|
+
float midY = (event.getY(0) + event.getY(1)) / 2;
|
|
393
|
+
|
|
394
|
+
// Convert midpoint from view coordinates to screen coordinates
|
|
395
|
+
float screenMidX = mLayoutParams.x + midX;
|
|
396
|
+
float screenMidY = mLayoutParams.y + midY;
|
|
397
|
+
|
|
398
|
+
float scale = newDist / oldDist;
|
|
399
|
+
int oldWidth = mLayoutParams.width;
|
|
400
|
+
int oldHeight = mLayoutParams.height;
|
|
401
|
+
int newWidth = (int)(oldWidth * scale);
|
|
402
|
+
int newHeight = (int)(oldHeight * scale);
|
|
403
|
+
|
|
404
|
+
// Use pre-calculated screen metrics
|
|
405
|
+
// Screen metrics are now updated in onTouch ACTION_DOWN, so they
|
|
406
|
+
// should be current here.
|
|
407
|
+
|
|
408
|
+
// Min size constraints: 1/2 of initial size, but not smaller than a
|
|
409
|
+
// fraction of screen respecting margins
|
|
410
|
+
int minAllowedWidth;
|
|
411
|
+
int minAllowedHeight;
|
|
412
|
+
if (isPortraitScreen) {
|
|
413
|
+
minAllowedWidth = Math.max(initialWidth / 2, drawableWidth / 2);
|
|
414
|
+
minAllowedHeight = (int)(minAllowedWidth / initialAspectRatio);
|
|
415
|
+
} else {
|
|
416
|
+
minAllowedHeight =
|
|
417
|
+
Math.max(initialHeight / 2, drawableHeight / 2);
|
|
418
|
+
minAllowedWidth = (int)(minAllowedHeight * initialAspectRatio);
|
|
419
|
+
}
|
|
420
|
+
// Ensure min dimensions are at least 1
|
|
421
|
+
minAllowedWidth = Math.max(1, minAllowedWidth);
|
|
422
|
+
minAllowedHeight = Math.max(1, minAllowedHeight);
|
|
423
|
+
|
|
424
|
+
// Max size constraints based on orientation and margins
|
|
425
|
+
int maxAllowedWidth, maxAllowedHeight;
|
|
426
|
+
if (isPortraitScreen) {
|
|
427
|
+
maxAllowedWidth = drawableWidth;
|
|
428
|
+
maxAllowedHeight =
|
|
429
|
+
(int)(maxAllowedWidth /
|
|
430
|
+
initialAspectRatio); // Maintain aspect ratio
|
|
431
|
+
if (maxAllowedHeight >
|
|
432
|
+
drawableHeight) { // If calculated height is too much for
|
|
433
|
+
// portrait
|
|
434
|
+
maxAllowedHeight = drawableHeight;
|
|
435
|
+
maxAllowedWidth = (int)(maxAllowedHeight * initialAspectRatio);
|
|
436
|
+
}
|
|
437
|
+
} else { // Landscape screen
|
|
438
|
+
maxAllowedHeight = drawableHeight;
|
|
439
|
+
maxAllowedWidth =
|
|
440
|
+
(int)(maxAllowedHeight *
|
|
441
|
+
initialAspectRatio); // Maintain aspect ratio
|
|
442
|
+
if (maxAllowedWidth > drawableWidth) { // If calculated width is
|
|
443
|
+
// too much for landscape
|
|
444
|
+
maxAllowedWidth = drawableWidth;
|
|
445
|
+
maxAllowedHeight = (int)(maxAllowedWidth / initialAspectRatio);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
// maxAllowedWidth = Math.min(maxAllowedWidth, LONGER_SIDE_MAX_LEN);
|
|
449
|
+
// // Global max len - Removed maxAllowedHeight =
|
|
450
|
+
// Math.min(maxAllowedHeight, LONGER_SIDE_MAX_LEN); // Global max
|
|
451
|
+
// len - Removed
|
|
452
|
+
if (initialAspectRatio >= 1)
|
|
453
|
+
maxAllowedHeight = (int)(maxAllowedWidth / initialAspectRatio);
|
|
454
|
+
else
|
|
455
|
+
maxAllowedWidth = (int)(maxAllowedHeight * initialAspectRatio);
|
|
456
|
+
|
|
457
|
+
// Apply scaling
|
|
458
|
+
newWidth = (int)(oldWidth * scale);
|
|
459
|
+
newHeight = (int)(oldHeight * scale);
|
|
460
|
+
|
|
461
|
+
// Clamp to min/max size while maintaining aspect ratio
|
|
462
|
+
if (scale < 1) { // Shrinking
|
|
463
|
+
newWidth = Math.max(minAllowedWidth, newWidth);
|
|
464
|
+
newHeight = (int)(newWidth / initialAspectRatio);
|
|
465
|
+
if (newHeight < minAllowedHeight) {
|
|
466
|
+
newHeight = minAllowedHeight;
|
|
467
|
+
newWidth = (int)(newHeight * initialAspectRatio);
|
|
468
|
+
}
|
|
469
|
+
} else { // Expanding
|
|
470
|
+
newWidth = Math.min(maxAllowedWidth, newWidth);
|
|
471
|
+
newHeight = (int)(newWidth / initialAspectRatio);
|
|
472
|
+
if (newHeight > maxAllowedHeight) {
|
|
473
|
+
newHeight = maxAllowedHeight;
|
|
474
|
+
newWidth = (int)(newHeight * initialAspectRatio);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Final check to ensure dimensions are within absolute min/max
|
|
479
|
+
// bounds
|
|
480
|
+
newWidth =
|
|
481
|
+
Math.max(minAllowedWidth, Math.min(newWidth, maxAllowedWidth));
|
|
482
|
+
newHeight =
|
|
483
|
+
(int)(newWidth / initialAspectRatio); // Maintain aspect ratio
|
|
484
|
+
// Ensure height is also clamped correctly after width adjustment
|
|
485
|
+
if (newHeight < minAllowedHeight) {
|
|
486
|
+
newHeight = minAllowedHeight;
|
|
487
|
+
newWidth = (int)(newHeight * initialAspectRatio);
|
|
488
|
+
}
|
|
489
|
+
if (newHeight > maxAllowedHeight) {
|
|
490
|
+
newHeight = maxAllowedHeight;
|
|
491
|
+
newWidth = (int)(newHeight * initialAspectRatio);
|
|
492
|
+
}
|
|
493
|
+
// Re-clamp width just in case height clamping affected it and
|
|
494
|
+
// pushed it out of width bounds
|
|
495
|
+
newWidth =
|
|
496
|
+
Math.max(minAllowedWidth, Math.min(newWidth, maxAllowedWidth));
|
|
497
|
+
|
|
498
|
+
// Adjust position to keep the zoom centered on the midpoint
|
|
499
|
+
mLayoutParams.x = (int)(screenMidX - (midX * newWidth / oldWidth));
|
|
500
|
+
mLayoutParams.y =
|
|
501
|
+
(int)(screenMidY - (midY * newHeight / oldHeight));
|
|
502
|
+
|
|
503
|
+
mLayoutParams.width = newWidth;
|
|
504
|
+
mLayoutParams.height = newHeight;
|
|
505
|
+
|
|
506
|
+
// Prevent window from going off-screen after repositioning
|
|
507
|
+
// For Y, keep margin constraints
|
|
508
|
+
mLayoutParams.y = Math.max(
|
|
509
|
+
MARGIN_TOP,
|
|
510
|
+
Math.min(mLayoutParams.y,
|
|
511
|
+
screenHeight - mLayoutParams.height - MARGIN_BOTTOM));
|
|
512
|
+
// For X, constrain to screen physical edges only during zoom,
|
|
513
|
+
// let animateSnapToEdge handle final margin snapping.
|
|
514
|
+
mLayoutParams.x = Math.max(
|
|
515
|
+
0, // Screen physical left edge
|
|
516
|
+
Math.min(
|
|
517
|
+
mLayoutParams.x,
|
|
518
|
+
screenWidth -
|
|
519
|
+
mLayoutParams.width)); // Screen physical right edge
|
|
520
|
+
|
|
521
|
+
// 更新SurfaceView的布局参数
|
|
522
|
+
if (mSurfaceView != null) {
|
|
523
|
+
android.view.ViewGroup.LayoutParams surfaceParams =
|
|
524
|
+
mSurfaceView.getLayoutParams();
|
|
525
|
+
if (surfaceParams != null) {
|
|
526
|
+
surfaceParams.width = mLayoutParams.width;
|
|
527
|
+
surfaceParams.height = mLayoutParams.height;
|
|
528
|
+
mSurfaceView.setLayoutParams(surfaceParams);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
mWindowManager.updateViewLayout(view, mLayoutParams);
|
|
532
|
+
oldDist = newDist;
|
|
533
|
+
}
|
|
534
|
+
isDragging =
|
|
535
|
+
true; // Zooming implies dragging state for click handling
|
|
536
|
+
}
|
|
245
537
|
break;
|
|
246
538
|
|
|
247
539
|
default:
|
|
@@ -249,5 +541,392 @@ public class FloatingWindowService extends Service {
|
|
|
249
541
|
}
|
|
250
542
|
return true;
|
|
251
543
|
}
|
|
544
|
+
|
|
545
|
+
private float spacing(MotionEvent event) {
|
|
546
|
+
if (event.getPointerCount() < 2)
|
|
547
|
+
return 0f;
|
|
548
|
+
float x = event.getX(0) - event.getX(1);
|
|
549
|
+
float y = event.getY(0) - event.getY(1);
|
|
550
|
+
return (float)Math.sqrt(x * x + y * y);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
private void handleDoubleClick(View view) {
|
|
554
|
+
Log.d(TAG, "Double tap detected");
|
|
555
|
+
int targetWidth, targetHeight;
|
|
556
|
+
|
|
557
|
+
// Use pre-calculated screen metrics
|
|
558
|
+
// updateScreenMetrics(); // Ensure metrics are fresh if not updated in
|
|
559
|
+
// onTouch ACTION_DOWN Screen metrics are now updated in onTouch
|
|
560
|
+
// ACTION_DOWN, so they should be current here.
|
|
561
|
+
|
|
562
|
+
// Minimum size: 1/2 of initial size, but not smaller than a fraction of
|
|
563
|
+
// screen respecting margins
|
|
564
|
+
int minAllowedWidth = initialWidth / 2;
|
|
565
|
+
int minAllowedHeight = initialHeight / 2;
|
|
566
|
+
if (isPortraitScreen) {
|
|
567
|
+
minAllowedWidth = Math.max(minAllowedWidth, drawableWidth / 2);
|
|
568
|
+
minAllowedHeight = (int)(minAllowedWidth / initialAspectRatio);
|
|
569
|
+
} else {
|
|
570
|
+
minAllowedHeight = Math.max(minAllowedHeight, drawableHeight / 2);
|
|
571
|
+
minAllowedWidth = (int)(minAllowedHeight * initialAspectRatio);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Maximum size based on orientation and margins
|
|
575
|
+
int maxAllowedTargetWidth, maxAllowedTargetHeight;
|
|
576
|
+
if (isPortraitScreen) {
|
|
577
|
+
maxAllowedTargetWidth = drawableWidth;
|
|
578
|
+
maxAllowedTargetHeight =
|
|
579
|
+
(int)(maxAllowedTargetWidth / initialAspectRatio);
|
|
580
|
+
if (maxAllowedTargetHeight > drawableHeight) {
|
|
581
|
+
maxAllowedTargetHeight = drawableHeight;
|
|
582
|
+
maxAllowedTargetWidth =
|
|
583
|
+
(int)(maxAllowedTargetHeight * initialAspectRatio);
|
|
584
|
+
}
|
|
585
|
+
} else { // Landscape screen
|
|
586
|
+
maxAllowedTargetHeight = drawableHeight;
|
|
587
|
+
maxAllowedTargetWidth =
|
|
588
|
+
(int)(maxAllowedTargetHeight * initialAspectRatio);
|
|
589
|
+
if (maxAllowedTargetWidth > drawableWidth) {
|
|
590
|
+
maxAllowedTargetWidth = drawableWidth;
|
|
591
|
+
maxAllowedTargetHeight =
|
|
592
|
+
(int)(maxAllowedTargetWidth / initialAspectRatio);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
// Apply global LONGER_SIDE_MAX_LEN constraint - Removed
|
|
596
|
+
// if (initialAspectRatio >= 1) { // Landscape or square video
|
|
597
|
+
// maxAllowedTargetWidth =
|
|
598
|
+
// Math.min(maxAllowedTargetWidth, LONGER_SIDE_MAX_LEN);
|
|
599
|
+
// maxAllowedTargetHeight =
|
|
600
|
+
// (int)(maxAllowedTargetWidth / initialAspectRatio);
|
|
601
|
+
// } else { // Portrait video
|
|
602
|
+
// maxAllowedTargetHeight =
|
|
603
|
+
// Math.min(maxAllowedTargetHeight, LONGER_SIDE_MAX_LEN);
|
|
604
|
+
// maxAllowedTargetWidth =
|
|
605
|
+
// (int)(maxAllowedTargetHeight * initialAspectRatio);
|
|
606
|
+
// }
|
|
607
|
+
|
|
608
|
+
int currentX = mLayoutParams.x;
|
|
609
|
+
int currentY = mLayoutParams.y;
|
|
610
|
+
int currentWidth = mLayoutParams.width;
|
|
611
|
+
int currentHeight = mLayoutParams.height;
|
|
612
|
+
|
|
613
|
+
int finalTargetX = currentX;
|
|
614
|
+
int finalTargetY = currentY;
|
|
615
|
+
|
|
616
|
+
// 计算当前宽度与中间值的比较,决定缩放方向
|
|
617
|
+
int midWidth = (minAllowedWidth + maxAllowedTargetWidth) / 2;
|
|
618
|
+
if (currentWidth < midWidth) {
|
|
619
|
+
// 当前更接近最小值,放大到最大尺寸
|
|
620
|
+
targetWidth = maxAllowedTargetWidth;
|
|
621
|
+
targetHeight = maxAllowedTargetHeight;
|
|
622
|
+
Log.d(TAG, "Zooming in to max: " + targetWidth + "x" + targetHeight);
|
|
623
|
+
} else {
|
|
624
|
+
// 当前更接近最大值,缩小到最小尺寸
|
|
625
|
+
targetWidth = minAllowedWidth;
|
|
626
|
+
targetHeight = minAllowedHeight;
|
|
627
|
+
Log.d(TAG, "Zooming out to min: " + targetWidth + "x" + targetHeight);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Ensure target dimensions respect calculated min/max and screen
|
|
631
|
+
// boundaries with margins
|
|
632
|
+
targetWidth = Math.max(minAllowedWidth,
|
|
633
|
+
Math.min(targetWidth, maxAllowedTargetWidth));
|
|
634
|
+
targetHeight = (int)(targetWidth / initialAspectRatio);
|
|
635
|
+
if (targetHeight < minAllowedHeight) {
|
|
636
|
+
targetHeight = minAllowedHeight;
|
|
637
|
+
targetWidth = (int)(targetHeight * initialAspectRatio);
|
|
638
|
+
}
|
|
639
|
+
if (targetHeight > maxAllowedTargetHeight) {
|
|
640
|
+
targetHeight = maxAllowedTargetHeight;
|
|
641
|
+
targetWidth = (int)(targetHeight * initialAspectRatio);
|
|
642
|
+
}
|
|
643
|
+
// Final check for width after height adjustment
|
|
644
|
+
targetWidth = Math.max(minAllowedWidth,
|
|
645
|
+
Math.min(targetWidth, maxAllowedTargetWidth));
|
|
646
|
+
|
|
647
|
+
// Instantly apply new size and update layout BEFORE starting
|
|
648
|
+
// position/size animations This helps in reducing black bars by setting
|
|
649
|
+
// the target container size first.
|
|
650
|
+
mLayoutParams.width = targetWidth;
|
|
651
|
+
mLayoutParams.height = targetHeight;
|
|
652
|
+
if (mSmallWindowView != null &&
|
|
653
|
+
mSmallWindowView.getWindowToken() != null) {
|
|
654
|
+
mWindowManager.updateViewLayout(mSmallWindowView, mLayoutParams);
|
|
655
|
+
// Also update SurfaceView's layout params to match the new window size
|
|
656
|
+
// immediately
|
|
657
|
+
if (mSurfaceView != null) {
|
|
658
|
+
android.view.ViewGroup.LayoutParams surfaceParams =
|
|
659
|
+
mSurfaceView.getLayoutParams();
|
|
660
|
+
if (surfaceParams != null) {
|
|
661
|
+
surfaceParams.width = targetWidth;
|
|
662
|
+
surfaceParams.height = targetHeight;
|
|
663
|
+
mSurfaceView.setLayoutParams(surfaceParams);
|
|
664
|
+
mSurfaceView
|
|
665
|
+
.requestLayout(); // Crucial for SurfaceView to redraw correctly
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// Determine window corner for zoom anchor, considering margins
|
|
671
|
+
boolean isTop =
|
|
672
|
+
currentY < (screenHeight - MARGIN_TOP - MARGIN_BOTTOM) / 2 -
|
|
673
|
+
currentHeight / 2 + MARGIN_TOP;
|
|
674
|
+
boolean isLeft =
|
|
675
|
+
currentX < (screenWidth - MARGIN_LEFT - MARGIN_RIGHT) / 2 -
|
|
676
|
+
currentWidth / 2 + MARGIN_LEFT;
|
|
677
|
+
|
|
678
|
+
if (isLeft && isTop) { // Top-left
|
|
679
|
+
finalTargetX = MARGIN_LEFT;
|
|
680
|
+
finalTargetY = MARGIN_TOP;
|
|
681
|
+
} else if (!isLeft && isTop) { // Top-right
|
|
682
|
+
finalTargetX = screenWidth - targetWidth - MARGIN_RIGHT;
|
|
683
|
+
finalTargetY = MARGIN_TOP;
|
|
684
|
+
} else if (isLeft && !isTop) { // Bottom-left
|
|
685
|
+
finalTargetX = MARGIN_LEFT;
|
|
686
|
+
finalTargetY = screenHeight - targetHeight - MARGIN_BOTTOM;
|
|
687
|
+
} else { // Bottom-right
|
|
688
|
+
finalTargetX = screenWidth - targetWidth - MARGIN_RIGHT;
|
|
689
|
+
finalTargetY = screenHeight - targetHeight - MARGIN_BOTTOM;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// Ensure target position is within drawable area
|
|
693
|
+
finalTargetX = Math.max(
|
|
694
|
+
MARGIN_LEFT,
|
|
695
|
+
Math.min(finalTargetX, screenWidth - targetWidth - MARGIN_RIGHT));
|
|
696
|
+
finalTargetY = Math.max(
|
|
697
|
+
MARGIN_TOP,
|
|
698
|
+
Math.min(finalTargetY, screenHeight - targetHeight - MARGIN_BOTTOM));
|
|
699
|
+
|
|
700
|
+
final int finalTargetWidth = targetWidth;
|
|
701
|
+
final int finalTargetHeight = targetHeight;
|
|
702
|
+
|
|
703
|
+
// Make copies for use in animator listeners
|
|
704
|
+
final int animFinalTargetX = finalTargetX;
|
|
705
|
+
final int animFinalTargetY = finalTargetY;
|
|
706
|
+
|
|
707
|
+
ValueAnimator xAnimator = ValueAnimator.ofInt(currentX, finalTargetX);
|
|
708
|
+
xAnimator.setDuration(200);
|
|
709
|
+
xAnimator.addUpdateListener(animation -> {
|
|
710
|
+
mLayoutParams.x = (Integer)animation.getAnimatedValue();
|
|
711
|
+
mWindowManager.updateViewLayout(view, mLayoutParams);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
ValueAnimator yAnimator = ValueAnimator.ofInt(currentY, finalTargetY);
|
|
715
|
+
yAnimator.setDuration(200);
|
|
716
|
+
yAnimator.addUpdateListener(animation -> {
|
|
717
|
+
mLayoutParams.y = (Integer)animation.getAnimatedValue();
|
|
718
|
+
mWindowManager.updateViewLayout(view, mLayoutParams);
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
ValueAnimator widthAnimator =
|
|
722
|
+
ValueAnimator.ofInt(currentWidth, finalTargetWidth);
|
|
723
|
+
widthAnimator.setDuration(200);
|
|
724
|
+
widthAnimator.addUpdateListener(animation -> {
|
|
725
|
+
mLayoutParams.width = (Integer)animation.getAnimatedValue();
|
|
726
|
+
if (mSurfaceView != null) { // Ensure mSurfaceView is not null
|
|
727
|
+
android.view.ViewGroup.LayoutParams surfaceParams =
|
|
728
|
+
mSurfaceView.getLayoutParams();
|
|
729
|
+
if (surfaceParams != null) { // Ensure surfaceParams is not null
|
|
730
|
+
surfaceParams.width = mLayoutParams.width;
|
|
731
|
+
mSurfaceView.setLayoutParams(surfaceParams);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
mWindowManager.updateViewLayout(view, mLayoutParams);
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
ValueAnimator heightAnimator =
|
|
738
|
+
ValueAnimator.ofInt(currentHeight, finalTargetHeight);
|
|
739
|
+
heightAnimator.setDuration(200);
|
|
740
|
+
heightAnimator.addUpdateListener(animation -> {
|
|
741
|
+
mLayoutParams.height = (Integer)animation.getAnimatedValue();
|
|
742
|
+
if (mSurfaceView != null) { // Ensure mSurfaceView is not null
|
|
743
|
+
android.view.ViewGroup.LayoutParams surfaceParams =
|
|
744
|
+
mSurfaceView.getLayoutParams();
|
|
745
|
+
if (surfaceParams != null) { // Ensure surfaceParams is not null
|
|
746
|
+
surfaceParams.height = mLayoutParams.height;
|
|
747
|
+
mSurfaceView.setLayoutParams(surfaceParams);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
mWindowManager.updateViewLayout(view, mLayoutParams);
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
android.animation.AnimatorSet animatorSet =
|
|
754
|
+
new android.animation.AnimatorSet();
|
|
755
|
+
animatorSet.playTogether(xAnimator, yAnimator, widthAnimator,
|
|
756
|
+
heightAnimator);
|
|
757
|
+
// The isDoubleClickAnimating flag is now managed by
|
|
758
|
+
// FloatingOnTouchListener but we still need to set it within
|
|
759
|
+
// handleDoubleClick's AnimatorSet listeners to ensure it's active for the
|
|
760
|
+
// duration of this specific animation.
|
|
761
|
+
animatorSet.addListener(new android.animation.AnimatorListenerAdapter() {
|
|
762
|
+
@Override
|
|
763
|
+
public void onAnimationStart(android.animation.Animator animation) {
|
|
764
|
+
if (FloatingWindowService.this.mFloatingOnTouchListener != null) {
|
|
765
|
+
FloatingWindowService.this.mFloatingOnTouchListener
|
|
766
|
+
.isDoubleClickAnimating = true;
|
|
767
|
+
}
|
|
768
|
+
Log.d(TAG, "Double tap AnimatorSet started.");
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
@Override
|
|
772
|
+
public void onAnimationEnd(android.animation.Animator animation) {
|
|
773
|
+
if (FloatingWindowService.this.mFloatingOnTouchListener != null) {
|
|
774
|
+
FloatingWindowService.this.mFloatingOnTouchListener
|
|
775
|
+
.isDoubleClickAnimating = false;
|
|
776
|
+
}
|
|
777
|
+
// ValueAnimators update mLayoutParams continuously via their
|
|
778
|
+
// AnimatorUpdateListener. For safety, explicitly set final position
|
|
779
|
+
// values as the last update might not be exact.
|
|
780
|
+
mLayoutParams.x = animFinalTargetX;
|
|
781
|
+
mLayoutParams.y = animFinalTargetY;
|
|
782
|
+
// mLayoutParams.width and mLayoutParams.height should be at
|
|
783
|
+
// finalTargetWidth/Height due to widthAnimator and heightAnimator's
|
|
784
|
+
// updates.
|
|
785
|
+
|
|
786
|
+
// Perform a final update of the window layout with all parameters.
|
|
787
|
+
if (mSmallWindowView != null &&
|
|
788
|
+
mSmallWindowView.getWindowToken() != null) {
|
|
789
|
+
mWindowManager.updateViewLayout(mSmallWindowView, mLayoutParams);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// Resize SurfaceView (logic moved from postDelayed and refined)
|
|
793
|
+
if (mSurfaceView != null && mLayoutParams != null) {
|
|
794
|
+
android.view.ViewGroup.LayoutParams surfaceParams =
|
|
795
|
+
mSurfaceView.getLayoutParams();
|
|
796
|
+
if (surfaceParams != null) {
|
|
797
|
+
boolean changed = false;
|
|
798
|
+
if (surfaceParams.width != mLayoutParams.width) {
|
|
799
|
+
surfaceParams.width =
|
|
800
|
+
mLayoutParams.width; // mLayoutParams.width is at its final
|
|
801
|
+
// animated value
|
|
802
|
+
changed = true;
|
|
803
|
+
}
|
|
804
|
+
if (surfaceParams.height != mLayoutParams.height) {
|
|
805
|
+
surfaceParams.height =
|
|
806
|
+
mLayoutParams.height; // mLayoutParams.height is at its
|
|
807
|
+
// final animated value
|
|
808
|
+
changed = true;
|
|
809
|
+
}
|
|
810
|
+
if (changed) {
|
|
811
|
+
mSurfaceView.setLayoutParams(surfaceParams);
|
|
812
|
+
}
|
|
813
|
+
mSurfaceView
|
|
814
|
+
.requestLayout(); // Ensure the SurfaceView redraws correctly
|
|
815
|
+
Log.d(TAG,
|
|
816
|
+
"SurfaceView dimensions finalized in onAnimationEnd to: " +
|
|
817
|
+
mLayoutParams.width + "x" + mLayoutParams.height);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
Log.d(TAG, "Double tap AnimatorSet ended. Final state: x=" +
|
|
821
|
+
mLayoutParams.x + ", y=" + mLayoutParams.y + ", w=" +
|
|
822
|
+
mLayoutParams.width + ", h=" + mLayoutParams.height);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
@Override
|
|
826
|
+
public void onAnimationCancel(android.animation.Animator animation) {
|
|
827
|
+
if (FloatingWindowService.this.mFloatingOnTouchListener != null) {
|
|
828
|
+
FloatingWindowService.this.mFloatingOnTouchListener
|
|
829
|
+
.isDoubleClickAnimating = false;
|
|
830
|
+
}
|
|
831
|
+
// Animation was cancelled, so explicitly set all mLayoutParams to
|
|
832
|
+
// their final target values.
|
|
833
|
+
mLayoutParams.x = animFinalTargetX;
|
|
834
|
+
mLayoutParams.y = animFinalTargetY;
|
|
835
|
+
mLayoutParams.width = finalTargetWidth;
|
|
836
|
+
mLayoutParams.height = finalTargetHeight;
|
|
837
|
+
|
|
838
|
+
if (mSmallWindowView != null &&
|
|
839
|
+
mSmallWindowView.getWindowToken() != null) {
|
|
840
|
+
mWindowManager.updateViewLayout(mSmallWindowView, mLayoutParams);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// Resize SurfaceView to final target dimensions
|
|
844
|
+
if (mSurfaceView != null && mLayoutParams != null) {
|
|
845
|
+
android.view.ViewGroup.LayoutParams surfaceParams =
|
|
846
|
+
mSurfaceView.getLayoutParams();
|
|
847
|
+
if (surfaceParams != null) {
|
|
848
|
+
if (surfaceParams.width != mLayoutParams.width ||
|
|
849
|
+
surfaceParams.height != mLayoutParams.height) {
|
|
850
|
+
surfaceParams.width =
|
|
851
|
+
mLayoutParams.width; // Now finalTargetWidth
|
|
852
|
+
surfaceParams.height =
|
|
853
|
+
mLayoutParams.height; // Now finalTargetHeight
|
|
854
|
+
mSurfaceView.setLayoutParams(surfaceParams);
|
|
855
|
+
}
|
|
856
|
+
mSurfaceView
|
|
857
|
+
.requestLayout(); // Ensure the SurfaceView redraws correctly
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
Log.d(TAG,
|
|
861
|
+
"Double tap AnimatorSet cancelled. State set to targets: x=" +
|
|
862
|
+
mLayoutParams.x + ", y=" + mLayoutParams.y + ", w=" +
|
|
863
|
+
mLayoutParams.width + ", h=" + mLayoutParams.height);
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
animatorSet.start();
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
private void animateSnapToEdge(View view) {
|
|
870
|
+
// Use pre-calculated screen metrics
|
|
871
|
+
// updateScreenMetrics(); // Ensure metrics are fresh if not updated in
|
|
872
|
+
// onTouch ACTION_DOWN Screen metrics are now updated in onTouch
|
|
873
|
+
// ACTION_DOWN, so they should be current here.
|
|
874
|
+
final int screenWidth = this.screenWidth; // Make final for use in lambda
|
|
875
|
+
final int windowWidthAtAnimationStart =
|
|
876
|
+
mLayoutParams.width; // Capture width at start
|
|
877
|
+
|
|
878
|
+
int currentX =
|
|
879
|
+
mLayoutParams
|
|
880
|
+
.x; // This is the x at the moment animateSnapToEdge is called
|
|
881
|
+
int targetX;
|
|
882
|
+
|
|
883
|
+
// Determine targetX based on current position and captured width
|
|
884
|
+
if (currentX + windowWidthAtAnimationStart / 2 < screenWidth / 2) {
|
|
885
|
+
targetX = MARGIN_LEFT;
|
|
886
|
+
} else {
|
|
887
|
+
targetX = screenWidth - windowWidthAtAnimationStart - MARGIN_RIGHT;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
Log.i(TAG, "animateSnapToEdge: currentX=" + currentX +
|
|
891
|
+
", windowWidthAtAnimStart=" + windowWidthAtAnimationStart +
|
|
892
|
+
", screenWidth=" + screenWidth + ", calculatedTargetX=" +
|
|
893
|
+
targetX + ", currentY=" + mLayoutParams.y +
|
|
894
|
+
", currentHeight=" + mLayoutParams.height);
|
|
895
|
+
|
|
896
|
+
if (currentX == targetX) {
|
|
897
|
+
Log.i(TAG, "animateSnapToEdge: Already at targetX (" + targetX +
|
|
898
|
+
"). No animation needed.");
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
ValueAnimator snapAnimator = ValueAnimator.ofInt(currentX, targetX);
|
|
903
|
+
snapAnimator.setDuration(200);
|
|
904
|
+
snapAnimator.addUpdateListener(animation -> {
|
|
905
|
+
mLayoutParams.x = (Integer)animation.getAnimatedValue();
|
|
906
|
+
// Boundary checks using width captured at animation start
|
|
907
|
+
if (mLayoutParams.x < 0) { // Physical screen boundary
|
|
908
|
+
mLayoutParams.x = 0;
|
|
909
|
+
}
|
|
910
|
+
// Ensure the right edge does not go beyond screen width
|
|
911
|
+
if (mLayoutParams.x + windowWidthAtAnimationStart >
|
|
912
|
+
screenWidth) { // Physical screen boundary
|
|
913
|
+
mLayoutParams.x = screenWidth - windowWidthAtAnimationStart;
|
|
914
|
+
}
|
|
915
|
+
try {
|
|
916
|
+
if (mSmallWindowView != null &&
|
|
917
|
+
mSmallWindowView.getWindowToken() !=
|
|
918
|
+
null) { // Check if view is still attached
|
|
919
|
+
mWindowManager.updateViewLayout(view, mLayoutParams);
|
|
920
|
+
}
|
|
921
|
+
} catch (IllegalArgumentException e) {
|
|
922
|
+
Log.w(TAG, "animateSnapToEdge: View not attached? " + e.getMessage());
|
|
923
|
+
// Cancel animator if view is gone to prevent further errors
|
|
924
|
+
if (animation.isRunning()) {
|
|
925
|
+
animation.cancel();
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
snapAnimator.start();
|
|
930
|
+
}
|
|
252
931
|
}
|
|
253
932
|
}
|
package/lib/commonjs/index.js
CHANGED
|
@@ -4565,6 +4565,7 @@ var TVLManager = function () {
|
|
|
4565
4565
|
var _enableAudioFrameObserver_decorators;
|
|
4566
4566
|
var _setEnableSuperResolution_decorators;
|
|
4567
4567
|
var _setVolume_decorators;
|
|
4568
|
+
var _setEnableIgnoreAudioInterruption_decorators;
|
|
4568
4569
|
_classThis = /** @class */ (function () {
|
|
4569
4570
|
function TVLManager_1() {
|
|
4570
4571
|
/** {zh}
|
|
@@ -4872,6 +4873,9 @@ var TVLManager = function () {
|
|
|
4872
4873
|
TVLManager_1.prototype.setVolume = function (volume) {
|
|
4873
4874
|
throw new Error('not implement');
|
|
4874
4875
|
};
|
|
4876
|
+
TVLManager_1.prototype.setEnableIgnoreAudioInterruption = function (enable) {
|
|
4877
|
+
throw new Error('not implement');
|
|
4878
|
+
};
|
|
4875
4879
|
return TVLManager_1;
|
|
4876
4880
|
}());
|
|
4877
4881
|
__setFunctionName(_classThis, "TVLManager");
|
|
@@ -4907,6 +4911,7 @@ var TVLManager = function () {
|
|
|
4907
4911
|
_enableAudioFrameObserver_decorators = [NativeMethodSync('enableAudioFrameObserver:enableRendering:')];
|
|
4908
4912
|
_setEnableSuperResolution_decorators = [NativeMethodSync('setEnableSuperResolution:')];
|
|
4909
4913
|
_setVolume_decorators = [NativeMethodSync('setVolume:')];
|
|
4914
|
+
_setEnableIgnoreAudioInterruption_decorators = [NativeMethodSync('setEnableIgnoreAudioInterruption:')];
|
|
4910
4915
|
__esDecorate(_classThis, null, _static_setLogLevel_decorators, { kind: "method", name: "setLogLevel", static: true, private: false, access: { has: function (obj) { return "setLogLevel" in obj; }, get: function (obj) { return obj.setLogLevel; } }, metadata: _metadata }, null, _staticExtraInitializers);
|
|
4911
4916
|
__esDecorate(_classThis, null, _static_getVersion_decorators, { kind: "method", name: "getVersion", static: true, private: false, access: { has: function (obj) { return "getVersion" in obj; }, get: function (obj) { return obj.getVersion; } }, metadata: _metadata }, null, _staticExtraInitializers);
|
|
4912
4917
|
__esDecorate(_classThis, null, _static_setHttpDNSHostIP_decorators, { kind: "method", name: "setHttpDNSHostIP", static: true, private: false, access: { has: function (obj) { return "setHttpDNSHostIP" in obj; }, get: function (obj) { return obj.setHttpDNSHostIP; } }, metadata: _metadata }, null, _staticExtraInitializers);
|
|
@@ -4933,6 +4938,7 @@ var TVLManager = function () {
|
|
|
4933
4938
|
__esDecorate(_classThis, null, _enableAudioFrameObserver_decorators, { kind: "method", name: "enableAudioFrameObserver", static: false, private: false, access: { has: function (obj) { return "enableAudioFrameObserver" in obj; }, get: function (obj) { return obj.enableAudioFrameObserver; } }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
4934
4939
|
__esDecorate(_classThis, null, _setEnableSuperResolution_decorators, { kind: "method", name: "setEnableSuperResolution", static: false, private: false, access: { has: function (obj) { return "setEnableSuperResolution" in obj; }, get: function (obj) { return obj.setEnableSuperResolution; } }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
4935
4940
|
__esDecorate(_classThis, null, _setVolume_decorators, { kind: "method", name: "setVolume", static: false, private: false, access: { has: function (obj) { return "setVolume" in obj; }, get: function (obj) { return obj.setVolume; } }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
4941
|
+
__esDecorate(_classThis, null, _setEnableIgnoreAudioInterruption_decorators, { kind: "method", name: "setEnableIgnoreAudioInterruption", static: false, private: false, access: { has: function (obj) { return "setEnableIgnoreAudioInterruption" in obj; }, get: function (obj) { return obj.setEnableIgnoreAudioInterruption; } }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
4936
4942
|
__esDecorate(null, null, _observer_decorators, { kind: "field", name: "observer", static: false, private: false, access: { has: function (obj) { return "observer" in obj; }, get: function (obj) { return obj.observer; }, set: function (obj, value) { obj.observer = value; } }, metadata: _metadata }, _observer_initializers, _observer_extraInitializers);
|
|
4937
4943
|
__esDecorate(null, null, _playerView_decorators, { kind: "field", name: "playerView", static: false, private: false, access: { has: function (obj) { return "playerView" in obj; }, get: function (obj) { return obj.playerView; }, set: function (obj, value) { obj.playerView = value; } }, metadata: _metadata }, _playerView_initializers, _playerView_extraInitializers);
|
|
4938
4944
|
__esDecorate(null, null, _volume_decorators, { kind: "field", name: "volume", static: false, private: false, access: { has: function (obj) { return "volume" in obj; }, get: function (obj) { return obj.volume; }, set: function (obj, value) { obj.volume = value; } }, metadata: _metadata }, _volume_initializers, _volume_extraInitializers);
|
|
@@ -6723,38 +6729,6 @@ var VeLivePlayerStatistics = function () {
|
|
|
6723
6729
|
enumerable: false,
|
|
6724
6730
|
configurable: true
|
|
6725
6731
|
});
|
|
6726
|
-
Object.defineProperty(VeLivePlayerStatistics_1.prototype, "format", {
|
|
6727
|
-
/** {zh}
|
|
6728
|
-
* @brief 当前播放的视频格式。
|
|
6729
|
-
*
|
|
6730
|
-
*/
|
|
6731
|
-
/** {en}
|
|
6732
|
-
* @brief The video format.
|
|
6733
|
-
*
|
|
6734
|
-
*/
|
|
6735
|
-
get: function () {
|
|
6736
|
-
// TODO: implement
|
|
6737
|
-
throw new Error("not implement 'format'");
|
|
6738
|
-
},
|
|
6739
|
-
enumerable: false,
|
|
6740
|
-
configurable: true
|
|
6741
|
-
});
|
|
6742
|
-
Object.defineProperty(VeLivePlayerStatistics_1.prototype, "protocol", {
|
|
6743
|
-
/** {zh}
|
|
6744
|
-
* @brief 当前播放的传输协议。
|
|
6745
|
-
*
|
|
6746
|
-
*/
|
|
6747
|
-
/** {en}
|
|
6748
|
-
* @brief The transmission protocol of the live stream.
|
|
6749
|
-
*
|
|
6750
|
-
*/
|
|
6751
|
-
get: function () {
|
|
6752
|
-
// TODO: implement
|
|
6753
|
-
throw new Error("not implement 'protocol'");
|
|
6754
|
-
},
|
|
6755
|
-
enumerable: false,
|
|
6756
|
-
configurable: true
|
|
6757
|
-
});
|
|
6758
6732
|
Object.defineProperty(VeLivePlayerStatistics_1.prototype, "url", {
|
|
6759
6733
|
/** {zh}
|
|
6760
6734
|
* @brief 当前直播的播放地址。
|
|
@@ -11384,6 +11358,13 @@ extendsClassMethod(VeLivePlayer, 'destroy', function (origin) {
|
|
|
11384
11358
|
}); });
|
|
11385
11359
|
};
|
|
11386
11360
|
});
|
|
11361
|
+
extendsClassMethod(VeLivePlayer, 'setEnableIgnoreAudioInterruption', function () {
|
|
11362
|
+
return function setEnableIgnoreAudioInterruption(enable) {
|
|
11363
|
+
return runImpl(this, function () { }, function (engine) {
|
|
11364
|
+
engine.setEnableIgnoreAudioInterruption(enable);
|
|
11365
|
+
});
|
|
11366
|
+
};
|
|
11367
|
+
});
|
|
11387
11368
|
|
|
11388
11369
|
extendsClassMethod(android_VeLivePlayerObserver, 'onVideoSizeChanged', function (original) {
|
|
11389
11370
|
return function onVideoSizeChanged(player, width, height) {
|
|
@@ -11641,23 +11622,24 @@ function initAndroidPlayer(options) {
|
|
|
11641
11622
|
}
|
|
11642
11623
|
function initIOSPlayer(options) {
|
|
11643
11624
|
return __awaiter(this, void 0, void 0, function () {
|
|
11644
|
-
var viewId, option, player, config, uiView, multiObserver, manager;
|
|
11625
|
+
var viewId, option, iosPlayer, player, config, uiView, multiObserver, manager;
|
|
11645
11626
|
return __generator(this, function (_a) {
|
|
11646
11627
|
switch (_a.label) {
|
|
11647
11628
|
case 0:
|
|
11648
11629
|
viewId = options.viewId, option = __rest(options, ["viewId"]);
|
|
11649
|
-
|
|
11630
|
+
iosPlayer = new TVLManager();
|
|
11631
|
+
player = packObject(iosPlayer, VeLivePlayer);
|
|
11650
11632
|
config = new VeLivePlayerConfiguration();
|
|
11651
11633
|
Object.assign(config, defaultConfig, option);
|
|
11652
11634
|
player.setConfig(config);
|
|
11653
11635
|
uiView = NativeView.getView(viewId, NativeUIView);
|
|
11654
|
-
return [4 /*yield*/, uiView.insertSubview(
|
|
11636
|
+
return [4 /*yield*/, uiView.insertSubview(iosPlayer.playerView, 0)];
|
|
11655
11637
|
case 1:
|
|
11656
11638
|
_a.sent();
|
|
11657
11639
|
multiObserver = VeLivePlayerMultiObserver.getInstance();
|
|
11658
|
-
multiObserver.setupPlayer(
|
|
11640
|
+
multiObserver.setupPlayer(iosPlayer);
|
|
11659
11641
|
manager = VeLivePictureInPictureManager.getInstance();
|
|
11660
|
-
manager.setupPlayer(
|
|
11642
|
+
manager.setupPlayer(iosPlayer);
|
|
11661
11643
|
return [2 /*return*/, player];
|
|
11662
11644
|
}
|
|
11663
11645
|
});
|
package/lib/module/index.js
CHANGED
|
@@ -4563,6 +4563,7 @@ var TVLManager = function () {
|
|
|
4563
4563
|
var _enableAudioFrameObserver_decorators;
|
|
4564
4564
|
var _setEnableSuperResolution_decorators;
|
|
4565
4565
|
var _setVolume_decorators;
|
|
4566
|
+
var _setEnableIgnoreAudioInterruption_decorators;
|
|
4566
4567
|
_classThis = /** @class */ (function () {
|
|
4567
4568
|
function TVLManager_1() {
|
|
4568
4569
|
/** {zh}
|
|
@@ -4870,6 +4871,9 @@ var TVLManager = function () {
|
|
|
4870
4871
|
TVLManager_1.prototype.setVolume = function (volume) {
|
|
4871
4872
|
throw new Error('not implement');
|
|
4872
4873
|
};
|
|
4874
|
+
TVLManager_1.prototype.setEnableIgnoreAudioInterruption = function (enable) {
|
|
4875
|
+
throw new Error('not implement');
|
|
4876
|
+
};
|
|
4873
4877
|
return TVLManager_1;
|
|
4874
4878
|
}());
|
|
4875
4879
|
__setFunctionName(_classThis, "TVLManager");
|
|
@@ -4905,6 +4909,7 @@ var TVLManager = function () {
|
|
|
4905
4909
|
_enableAudioFrameObserver_decorators = [NativeMethodSync('enableAudioFrameObserver:enableRendering:')];
|
|
4906
4910
|
_setEnableSuperResolution_decorators = [NativeMethodSync('setEnableSuperResolution:')];
|
|
4907
4911
|
_setVolume_decorators = [NativeMethodSync('setVolume:')];
|
|
4912
|
+
_setEnableIgnoreAudioInterruption_decorators = [NativeMethodSync('setEnableIgnoreAudioInterruption:')];
|
|
4908
4913
|
__esDecorate(_classThis, null, _static_setLogLevel_decorators, { kind: "method", name: "setLogLevel", static: true, private: false, access: { has: function (obj) { return "setLogLevel" in obj; }, get: function (obj) { return obj.setLogLevel; } }, metadata: _metadata }, null, _staticExtraInitializers);
|
|
4909
4914
|
__esDecorate(_classThis, null, _static_getVersion_decorators, { kind: "method", name: "getVersion", static: true, private: false, access: { has: function (obj) { return "getVersion" in obj; }, get: function (obj) { return obj.getVersion; } }, metadata: _metadata }, null, _staticExtraInitializers);
|
|
4910
4915
|
__esDecorate(_classThis, null, _static_setHttpDNSHostIP_decorators, { kind: "method", name: "setHttpDNSHostIP", static: true, private: false, access: { has: function (obj) { return "setHttpDNSHostIP" in obj; }, get: function (obj) { return obj.setHttpDNSHostIP; } }, metadata: _metadata }, null, _staticExtraInitializers);
|
|
@@ -4931,6 +4936,7 @@ var TVLManager = function () {
|
|
|
4931
4936
|
__esDecorate(_classThis, null, _enableAudioFrameObserver_decorators, { kind: "method", name: "enableAudioFrameObserver", static: false, private: false, access: { has: function (obj) { return "enableAudioFrameObserver" in obj; }, get: function (obj) { return obj.enableAudioFrameObserver; } }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
4932
4937
|
__esDecorate(_classThis, null, _setEnableSuperResolution_decorators, { kind: "method", name: "setEnableSuperResolution", static: false, private: false, access: { has: function (obj) { return "setEnableSuperResolution" in obj; }, get: function (obj) { return obj.setEnableSuperResolution; } }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
4933
4938
|
__esDecorate(_classThis, null, _setVolume_decorators, { kind: "method", name: "setVolume", static: false, private: false, access: { has: function (obj) { return "setVolume" in obj; }, get: function (obj) { return obj.setVolume; } }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
4939
|
+
__esDecorate(_classThis, null, _setEnableIgnoreAudioInterruption_decorators, { kind: "method", name: "setEnableIgnoreAudioInterruption", static: false, private: false, access: { has: function (obj) { return "setEnableIgnoreAudioInterruption" in obj; }, get: function (obj) { return obj.setEnableIgnoreAudioInterruption; } }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
4934
4940
|
__esDecorate(null, null, _observer_decorators, { kind: "field", name: "observer", static: false, private: false, access: { has: function (obj) { return "observer" in obj; }, get: function (obj) { return obj.observer; }, set: function (obj, value) { obj.observer = value; } }, metadata: _metadata }, _observer_initializers, _observer_extraInitializers);
|
|
4935
4941
|
__esDecorate(null, null, _playerView_decorators, { kind: "field", name: "playerView", static: false, private: false, access: { has: function (obj) { return "playerView" in obj; }, get: function (obj) { return obj.playerView; }, set: function (obj, value) { obj.playerView = value; } }, metadata: _metadata }, _playerView_initializers, _playerView_extraInitializers);
|
|
4936
4942
|
__esDecorate(null, null, _volume_decorators, { kind: "field", name: "volume", static: false, private: false, access: { has: function (obj) { return "volume" in obj; }, get: function (obj) { return obj.volume; }, set: function (obj, value) { obj.volume = value; } }, metadata: _metadata }, _volume_initializers, _volume_extraInitializers);
|
|
@@ -6721,38 +6727,6 @@ var VeLivePlayerStatistics = function () {
|
|
|
6721
6727
|
enumerable: false,
|
|
6722
6728
|
configurable: true
|
|
6723
6729
|
});
|
|
6724
|
-
Object.defineProperty(VeLivePlayerStatistics_1.prototype, "format", {
|
|
6725
|
-
/** {zh}
|
|
6726
|
-
* @brief 当前播放的视频格式。
|
|
6727
|
-
*
|
|
6728
|
-
*/
|
|
6729
|
-
/** {en}
|
|
6730
|
-
* @brief The video format.
|
|
6731
|
-
*
|
|
6732
|
-
*/
|
|
6733
|
-
get: function () {
|
|
6734
|
-
// TODO: implement
|
|
6735
|
-
throw new Error("not implement 'format'");
|
|
6736
|
-
},
|
|
6737
|
-
enumerable: false,
|
|
6738
|
-
configurable: true
|
|
6739
|
-
});
|
|
6740
|
-
Object.defineProperty(VeLivePlayerStatistics_1.prototype, "protocol", {
|
|
6741
|
-
/** {zh}
|
|
6742
|
-
* @brief 当前播放的传输协议。
|
|
6743
|
-
*
|
|
6744
|
-
*/
|
|
6745
|
-
/** {en}
|
|
6746
|
-
* @brief The transmission protocol of the live stream.
|
|
6747
|
-
*
|
|
6748
|
-
*/
|
|
6749
|
-
get: function () {
|
|
6750
|
-
// TODO: implement
|
|
6751
|
-
throw new Error("not implement 'protocol'");
|
|
6752
|
-
},
|
|
6753
|
-
enumerable: false,
|
|
6754
|
-
configurable: true
|
|
6755
|
-
});
|
|
6756
6730
|
Object.defineProperty(VeLivePlayerStatistics_1.prototype, "url", {
|
|
6757
6731
|
/** {zh}
|
|
6758
6732
|
* @brief 当前直播的播放地址。
|
|
@@ -11382,6 +11356,13 @@ extendsClassMethod(VeLivePlayer, 'destroy', function (origin) {
|
|
|
11382
11356
|
}); });
|
|
11383
11357
|
};
|
|
11384
11358
|
});
|
|
11359
|
+
extendsClassMethod(VeLivePlayer, 'setEnableIgnoreAudioInterruption', function () {
|
|
11360
|
+
return function setEnableIgnoreAudioInterruption(enable) {
|
|
11361
|
+
return runImpl(this, function () { }, function (engine) {
|
|
11362
|
+
engine.setEnableIgnoreAudioInterruption(enable);
|
|
11363
|
+
});
|
|
11364
|
+
};
|
|
11365
|
+
});
|
|
11385
11366
|
|
|
11386
11367
|
extendsClassMethod(android_VeLivePlayerObserver, 'onVideoSizeChanged', function (original) {
|
|
11387
11368
|
return function onVideoSizeChanged(player, width, height) {
|
|
@@ -11639,23 +11620,24 @@ function initAndroidPlayer(options) {
|
|
|
11639
11620
|
}
|
|
11640
11621
|
function initIOSPlayer(options) {
|
|
11641
11622
|
return __awaiter(this, void 0, void 0, function () {
|
|
11642
|
-
var viewId, option, player, config, uiView, multiObserver, manager;
|
|
11623
|
+
var viewId, option, iosPlayer, player, config, uiView, multiObserver, manager;
|
|
11643
11624
|
return __generator(this, function (_a) {
|
|
11644
11625
|
switch (_a.label) {
|
|
11645
11626
|
case 0:
|
|
11646
11627
|
viewId = options.viewId, option = __rest(options, ["viewId"]);
|
|
11647
|
-
|
|
11628
|
+
iosPlayer = new TVLManager();
|
|
11629
|
+
player = packObject(iosPlayer, VeLivePlayer);
|
|
11648
11630
|
config = new VeLivePlayerConfiguration();
|
|
11649
11631
|
Object.assign(config, defaultConfig, option);
|
|
11650
11632
|
player.setConfig(config);
|
|
11651
11633
|
uiView = NativeView.getView(viewId, NativeUIView);
|
|
11652
|
-
return [4 /*yield*/, uiView.insertSubview(
|
|
11634
|
+
return [4 /*yield*/, uiView.insertSubview(iosPlayer.playerView, 0)];
|
|
11653
11635
|
case 1:
|
|
11654
11636
|
_a.sent();
|
|
11655
11637
|
multiObserver = VeLivePlayerMultiObserver.getInstance();
|
|
11656
|
-
multiObserver.setupPlayer(
|
|
11638
|
+
multiObserver.setupPlayer(iosPlayer);
|
|
11657
11639
|
manager = VeLivePictureInPictureManager.getInstance();
|
|
11658
|
-
manager.setupPlayer(
|
|
11640
|
+
manager.setupPlayer(iosPlayer);
|
|
11659
11641
|
return [2 /*return*/, player];
|
|
11660
11642
|
}
|
|
11661
11643
|
});
|
|
@@ -111,18 +111,6 @@ export declare class VeLivePlayerStatistics {
|
|
|
111
111
|
*/
|
|
112
112
|
get isHardwareDecode(): boolean;
|
|
113
113
|
|
|
114
|
-
/** {en}
|
|
115
|
-
* @brief The video format.
|
|
116
|
-
*
|
|
117
|
-
*/
|
|
118
|
-
get format(): void;
|
|
119
|
-
|
|
120
|
-
/** {en}
|
|
121
|
-
* @brief The transmission protocol of the live stream.
|
|
122
|
-
*
|
|
123
|
-
*/
|
|
124
|
-
get protocol(): void;
|
|
125
|
-
|
|
126
114
|
/** {en}
|
|
127
115
|
* @brief The current pull stream address.
|
|
128
116
|
*
|
|
@@ -83,6 +83,19 @@ declare module '../codegen/pack/api' {
|
|
|
83
83
|
* @order 6
|
|
84
84
|
*/
|
|
85
85
|
disablePictureInPicture(): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* {zh}
|
|
88
|
+
* @brief 设置是否忽略音频中断
|
|
89
|
+
* @param enable 是否忽略音频中断
|
|
90
|
+
* @order 7
|
|
91
|
+
*/
|
|
92
|
+
/**
|
|
93
|
+
* {en}
|
|
94
|
+
* @brief Set whether to ignore audio interruption
|
|
95
|
+
* @param enable Whether to ignore audio interruption
|
|
96
|
+
* @order 7
|
|
97
|
+
*/
|
|
98
|
+
setEnableIgnoreAudioInterruption(enable: boolean): void;
|
|
86
99
|
}
|
|
87
100
|
}
|
|
88
101
|
export { VeLivePlayer };
|
|
@@ -1 +1,17 @@
|
|
|
1
|
+
declare module '../codegen/pack/keytype' {
|
|
2
|
+
interface VeLivePlayerStatistics {
|
|
3
|
+
|
|
4
|
+
/** {en}
|
|
5
|
+
* @brief The video format.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
format: VeLivePlayerFormat;
|
|
9
|
+
|
|
10
|
+
/** {en}
|
|
11
|
+
* @brief The transmission protocol of the live stream.
|
|
12
|
+
*
|
|
13
|
+
*/
|
|
14
|
+
protocol: VeLivePlayerProtocol;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
1
17
|
export { VeLivePlayerAudioBufferType, VeLivePlayerAudioFrame, VeLivePlayerConfiguration, VeLivePlayerFillMode, VeLivePlayerFormat, VeLivePlayerLogLevel, VeLivePlayerMirror, VeLivePlayerPixelFormat, VeLivePlayerProtocol, VeLivePlayerResolution, VeLivePlayerResolutionSwitchReason, VeLivePlayerRotation, VeLivePlayerStatistics, VeLivePlayerStatus, VeLivePlayerStream, VeLivePlayerStreamData, VeLivePlayerStreamType, VeLivePlayerVideoBufferType, VeLivePlayerVideoFrame, } from '../codegen/pack/keytype';
|