@byteplus/react-native-live-pull 1.0.3-rc.1 → 1.1.1-rc.1

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.
Files changed (28) hide show
  1. package/android/src/main/AndroidManifest.xml +7 -1
  2. package/android/src/main/AndroidManifestNew.xml +15 -1
  3. package/android/src/main/java/com/volcengine/velive/rn/pull/VolcLiveModule.java +28 -20
  4. package/android/src/main/java/com/volcengine/velive/rn/pull/VolcView.java +7 -8
  5. package/android/src/main/java/com/volcengine/velive/rn/pull/pictureInpicture/FloatingWindowHelper.java +224 -0
  6. package/android/src/main/java/com/volcengine/velive/rn/pull/pictureInpicture/FloatingWindowService.java +198 -0
  7. package/android/src/main/java/com/volcengine/velive/rn/pull/pictureInpicture/IFloatingWindowHelper.java +80 -0
  8. package/android/src/main/java/com/volcengine/velive/rn/pull/pictureInpicture/PictureInPictureManager.java +257 -0
  9. package/android/src/main/java/com/volcengine/velive/rn/pull/pictureInpicture/VeLiveRefManager.java +119 -0
  10. package/android/src/main/res/drawable/button_close.xml +9 -0
  11. package/android/src/main/res/drawable/new_window.xml +16 -0
  12. package/android/src/main/res/layout/floating_window_layout.xml +29 -0
  13. package/ios/VeLivePlayerMultiObserver.h +54 -0
  14. package/ios/VeLivePlayerMultiObserver.m +324 -0
  15. package/ios/pictureInpicture/PictureInPictureManager.h +29 -0
  16. package/ios/pictureInpicture/PictureInPictureManager.m +274 -0
  17. package/ios/pictureInpicture/VeLivePictureInPictureController.h +207 -0
  18. package/ios/pictureInpicture/VeLivePictureInPictureController.m +3393 -0
  19. package/lib/commonjs/index.js +1690 -1955
  20. package/lib/module/index.js +1690 -1955
  21. package/lib/typescript/codegen/pack/errorcode.d.ts +24 -24
  22. package/lib/typescript/codegen/pack/types.d.ts +1 -1
  23. package/lib/typescript/core/api.d.ts +88 -1
  24. package/lib/typescript/core/callback.d.ts +52 -0
  25. package/lib/typescript/platforms/android/extends.d.ts +1 -1
  26. package/lib/typescript/platforms/android/pictureInpicture.d.ts +26 -0
  27. package/lib/typescript/platforms/ios/pictureInpicture.d.ts +32 -0
  28. package/package.json +1 -1
@@ -6,4 +6,10 @@
6
6
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
7
7
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
8
8
 
9
- </manifest>
9
+ <application>
10
+ <activity
11
+ android:name="com.volcengine.velive.rn.pull.pictureInpicture.AssistantActivity"
12
+ android:exported="true"
13
+ android:theme="@style/AppTheme.NoActionBar" />
14
+ </application>
15
+ </manifest>
@@ -4,5 +4,19 @@
4
4
  <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
5
5
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
6
6
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
7
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
7
8
 
8
- </manifest>
9
+ <application>
10
+ <activity
11
+ android:name="com.volcengine.velive.rn.pull.pictureInpicture.FloatingWindowHelper$AssistantActivity"
12
+ android:exported="true"
13
+ />
14
+
15
+ <service
16
+ android:name="com.volcengine.velive.rn.pull.pictureInpicture.FloatingWindowService"
17
+ android:enabled="true"
18
+ android:exported="false"
19
+ android:foregroundServiceType="mediaProjection" />
20
+ </application>
21
+
22
+ </manifest>
@@ -1,7 +1,6 @@
1
1
  package com.volcengine.velive.rn.pull;
2
2
 
3
3
  import androidx.annotation.Nullable;
4
-
5
4
  import com.facebook.react.bridge.Arguments;
6
5
  import com.facebook.react.bridge.Callback;
7
6
  import com.facebook.react.bridge.ReactApplicationContext;
@@ -11,21 +10,21 @@ import com.facebook.react.bridge.ReadableMap;
11
10
  import com.facebook.react.bridge.WritableMap;
12
11
  import com.facebook.react.module.annotations.ReactModule;
13
12
  import com.facebook.react.modules.core.DeviceEventManagerModule;
14
-
15
13
  import com.volcengine.VolcApiEngine.*;
16
14
  import com.volcengine.velive.rn.pull.autogen.MethodSignature;
17
15
 
18
16
  @ReactModule(name = VolcLiveModule.NAME)
19
- public class VolcLiveModule extends VolcLiveModuleSpec implements IEventReceiver {
17
+ public class VolcLiveModule
18
+ extends VolcLiveModuleSpec implements IEventReceiver {
20
19
  public static final String NAME = "VolcLiveModule";
21
-
20
+
22
21
  VolcApiEngine apiEngine = null;
23
-
22
+
24
23
  VolcLiveModule(ReactApplicationContext context) {
25
24
  super(context);
26
25
  MethodSignature.init();
27
26
  }
28
-
27
+
29
28
  @Override
30
29
  public String getName() {
31
30
  return NAME;
@@ -38,12 +37,12 @@ public class VolcLiveModule extends VolcLiveModuleSpec implements IEventReceiver
38
37
  NativeVariableManager.init(apiEngine.msgClient, super.context);
39
38
  return true;
40
39
  }
41
-
40
+
42
41
  return false;
43
42
  }
44
43
 
45
44
  @ReactMethod(isBlockingSynchronousMethod = true)
46
- public String callApiSync(ReadableMap arg) {
45
+ public String callApiSync(ReadableMap arg) {
47
46
  try {
48
47
  newApiEngine();
49
48
  String params = arg.getString("params");
@@ -55,16 +54,26 @@ public class VolcLiveModule extends VolcLiveModuleSpec implements IEventReceiver
55
54
  }
56
55
 
57
56
  @ReactMethod(isBlockingSynchronousMethod = false)
58
- public void callApi(ReadableMap arg, Callback callback) {
57
+ public void callApi(ReadableMap arg, Callback callback) {
59
58
  try {
60
59
  newApiEngine();
61
60
  String params = arg.getString("params");
62
- this.apiEngine.callApi(params, (res) -> {
63
- callback.invoke(res.toJsonString());
64
- });
61
+
62
+ // 获取主线程处理器
63
+ android.os.Handler mainHandler =
64
+ new android.os.Handler(android.os.Looper.getMainLooper());
65
+
66
+ // 在主线程上执行 API 调用
67
+ mainHandler.post(() -> {
68
+ try {
69
+ this.apiEngine.callApi(
70
+ params, (res) -> { callback.invoke(res.toJsonString()); });
71
+ } catch (Exception e) {
72
+ e.printStackTrace();
73
+ }
74
+ });
65
75
  } catch (Exception e) {
66
76
  e.printStackTrace();
67
- throw new RuntimeException(e);
68
77
  }
69
78
  }
70
79
 
@@ -87,12 +96,11 @@ public class VolcLiveModule extends VolcLiveModuleSpec implements IEventReceiver
87
96
  }
88
97
  }
89
98
 
90
- private void sendEvent(ReactContext reactContext,
91
- String eventName,
92
- @Nullable WritableMap params) {
99
+ private void sendEvent(ReactContext reactContext, String eventName,
100
+ @Nullable WritableMap params) {
93
101
  reactContext
94
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
95
- .emit(eventName, params);
102
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
103
+ .emit(eventName, params);
96
104
  }
97
105
 
98
106
  @Override
@@ -102,7 +110,7 @@ public class VolcLiveModule extends VolcLiveModuleSpec implements IEventReceiver
102
110
  map.putString("data", data);
103
111
 
104
112
  super.context
105
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
106
- .emit(VolcLiveModuleSpec.EVENT, map);
113
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
114
+ .emit(VolcLiveModuleSpec.EVENT, map);
107
115
  }
108
116
  }
@@ -2,7 +2,6 @@ package com.volcengine.velive.rn.pull;
2
2
 
3
3
  import android.content.Context;
4
4
  import android.widget.FrameLayout;
5
-
6
5
  import com.facebook.react.bridge.Arguments;
7
6
  import com.facebook.react.bridge.ReactContext;
8
7
  import com.facebook.react.bridge.WritableMap;
@@ -11,13 +10,14 @@ import com.facebook.react.uimanager.events.RCTEventEmitter;
11
10
  public class VolcView extends FrameLayout {
12
11
  public String viewId;
13
12
  public boolean hasRegister = false;
14
-
15
- public VolcView(Context context) {
13
+
14
+ public VolcView(Context context) {
16
15
  super(context);
16
+ this.setKeepScreenOn(true);
17
17
  }
18
-
18
+
19
19
  public void setViewId(String viewId) {
20
- this.viewId = viewId;
20
+ this.viewId = viewId;
21
21
  this.hasRegister = true;
22
22
  this.emitOnLoad();
23
23
  }
@@ -25,8 +25,7 @@ public class VolcView extends FrameLayout {
25
25
  public void emitOnLoad() {
26
26
  WritableMap event = Arguments.createMap();
27
27
  ReactContext reactContext = (ReactContext)getContext();
28
- reactContext
29
- .getJSModule(RCTEventEmitter.class)
30
- .receiveEvent(getId(), "load", event);
28
+ reactContext.getJSModule(RCTEventEmitter.class)
29
+ .receiveEvent(getId(), "load", event);
31
30
  }
32
31
  }
@@ -0,0 +1,224 @@
1
+ package com.volcengine.velive.rn.pull.pictureInpicture;
2
+
3
+ import static com.volcengine.velive.rn.pull.pictureInpicture.FloatingWindowService.INTENT_EXTRA_KEY_ASPECT_RATIO;
4
+ import static com.volcengine.velive.rn.pull.pictureInpicture.FloatingWindowService.INTENT_EXTRA_KEY_X_POS;
5
+ import static com.volcengine.velive.rn.pull.pictureInpicture.FloatingWindowService.INTENT_EXTRA_KEY_Y_POS;
6
+ import static com.volcengine.velive.rn.pull.pictureInpicture.IFloatingWindowHelper.Listener.ERR_INVALID_PARAMS;
7
+ import static com.volcengine.velive.rn.pull.pictureInpicture.IFloatingWindowHelper.Listener.ERR_IS_ALREADY_OPEN;
8
+ import static com.volcengine.velive.rn.pull.pictureInpicture.IFloatingWindowHelper.Listener.ERR_NO;
9
+ import static com.volcengine.velive.rn.pull.pictureInpicture.IFloatingWindowHelper.Listener.ERR_NOT_SUPPORT;
10
+ import static com.volcengine.velive.rn.pull.pictureInpicture.IFloatingWindowHelper.Listener.ERR_RETRY;
11
+
12
+ import android.app.Activity;
13
+ import android.content.Context;
14
+ import android.content.Intent;
15
+ import android.net.Uri;
16
+ import android.os.Build;
17
+ import android.os.Bundle;
18
+ import android.provider.Settings;
19
+ import android.view.SurfaceView;
20
+ import androidx.annotation.Nullable;
21
+ import java.util.HashMap;
22
+ import java.util.Map;
23
+
24
+ public class FloatingWindowHelper implements IFloatingWindowHelper {
25
+ private static FloatingWindowHelper mInstance = null;
26
+ private Listener mListener;
27
+ private Config mConfig;
28
+ private Map<String, Object> mExtraDataMap;
29
+ private boolean isRunning;
30
+
31
+ public synchronized static FloatingWindowHelper getInstance() {
32
+ if (mInstance == null) {
33
+ mInstance = new FloatingWindowHelper();
34
+ }
35
+ return mInstance;
36
+ }
37
+
38
+ public static boolean isPictureInPictureSupported() {
39
+ // Check if the system version supports floating windows
40
+ if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
41
+ // Android versions below 6.0 don't support SYSTEM_ALERT_WINDOW permission
42
+ // management
43
+ return false;
44
+ }
45
+ // The system supports PIP mode
46
+ return true;
47
+ }
48
+
49
+ @Override
50
+ public void setEventListener(Listener listener) {
51
+ mListener = listener;
52
+ }
53
+
54
+ public Listener getEventListener() { return mListener; }
55
+
56
+ public void openFloatingWindow(Context context, Config config,
57
+ Map<String, Object> extraData) {
58
+ if (isRunning) {
59
+ if (mListener != null) {
60
+ mListener.onOpenFloatingWindowResult(ERR_IS_ALREADY_OPEN, extraData);
61
+ }
62
+ return;
63
+ }
64
+ // Invalid parameters
65
+ if (context == null || config.aspectRatio <= 0) {
66
+ if (mListener != null) {
67
+ mListener.onOpenFloatingWindowResult(ERR_INVALID_PARAMS, extraData);
68
+ }
69
+ return;
70
+ }
71
+
72
+ // Android versions below 6.0 don't support Overlay functionality
73
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
74
+ if (mListener != null) {
75
+ mListener.onOpenFloatingWindowResult(ERR_NOT_SUPPORT, extraData);
76
+ }
77
+ return;
78
+ }
79
+
80
+ // Save configuration first, so it can be used in permission request
81
+ // callback
82
+ mConfig = config;
83
+ mExtraDataMap = extraData;
84
+
85
+ // If Overlay permission is not enabled, request it first
86
+ if (!Settings.canDrawOverlays(context)) {
87
+ if (mListener != null && mListener.onRequestOverlayPermission()) {
88
+ requestOverlayPermission(context);
89
+ } else {
90
+ // User doesn't allow permission request, trigger error callback
91
+ if (mListener != null) {
92
+ mListener.onOpenFloatingWindowResult(ERR_INVALID_PARAMS, extraData);
93
+ }
94
+ }
95
+ return;
96
+ }
97
+
98
+ Intent intent = new Intent(context, FloatingWindowService.class);
99
+ intent.putExtra(INTENT_EXTRA_KEY_ASPECT_RATIO, mConfig.aspectRatio);
100
+ intent.putExtra(INTENT_EXTRA_KEY_X_POS, mConfig.x);
101
+ intent.putExtra(INTENT_EXTRA_KEY_Y_POS, mConfig.y);
102
+ context.startService(intent);
103
+ isRunning = true;
104
+ }
105
+
106
+ @Override
107
+ public void closeFloatingWindow(Context context) {
108
+ if (!isRunning) {
109
+ return;
110
+ }
111
+ context.stopService(new Intent(context, FloatingWindowService.class));
112
+ isRunning = false;
113
+ }
114
+
115
+ @Override
116
+ public void requestOverlayPermission(Context context) {
117
+ try {
118
+ Intent intent = new Intent(context, AssistantActivity.class);
119
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
120
+ context.startActivity(intent);
121
+ } catch (Exception e) {
122
+ // If starting AssistantActivity fails, trigger error callback
123
+ if (mListener != null) {
124
+ Map<String, Object> errorData = new HashMap<>();
125
+ errorData.put("error", e.getMessage());
126
+ mListener.onOpenFloatingWindowResult(ERR_NOT_SUPPORT, errorData);
127
+ }
128
+ }
129
+ }
130
+
131
+ @Override
132
+ public Config getConfig() {
133
+ return mConfig;
134
+ }
135
+
136
+ @Override
137
+ public Map<String, Object> getExtraData() {
138
+ return mExtraDataMap;
139
+ }
140
+
141
+ @Override
142
+ public boolean isOpen() {
143
+ return isRunning;
144
+ }
145
+
146
+ void setSurfaceView(SurfaceView surfaceView) {
147
+ if (mListener != null) {
148
+ mListener.onUpdateSurfaceView(surfaceView);
149
+ }
150
+ }
151
+
152
+ void performClose(Context context) {
153
+ if (mListener != null) {
154
+ mListener.onClickFloatingWindowCloseBtn(context);
155
+ }
156
+ }
157
+
158
+ void onClickFloatingWindow(Context context) {
159
+ if (mListener != null) {
160
+ mListener.onClickFloatingWindow(context);
161
+ }
162
+ }
163
+
164
+ void onStartService() {
165
+ if (mListener != null) {
166
+ mListener.onOpenFloatingWindowResult(ERR_NO, mExtraDataMap);
167
+ }
168
+ }
169
+
170
+ void onStopService() {
171
+ if (mListener != null) {
172
+ mListener.onCloseFloatingWindow(mExtraDataMap);
173
+ }
174
+
175
+ mConfig = null;
176
+ mExtraDataMap = null;
177
+ }
178
+
179
+ public static class AssistantActivity extends Activity {
180
+ private Listener mListener;
181
+
182
+ @Override
183
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
184
+ super.onCreate(savedInstanceState);
185
+ mListener = FloatingWindowHelper.getInstance().getEventListener();
186
+
187
+ int sdkInt = Build.VERSION.SDK_INT;
188
+ int mOverlayRequestCode = 1001;
189
+ if (sdkInt >= Build.VERSION_CODES.O) { // Android 8.0 and above
190
+ Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
191
+ startActivityForResult(intent, mOverlayRequestCode);
192
+ } else if (sdkInt >= Build.VERSION_CODES.M) {
193
+ Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
194
+ intent.setData(Uri.parse("package:" + getPackageName()));
195
+ startActivityForResult(intent, mOverlayRequestCode);
196
+ }
197
+ }
198
+
199
+ @Override
200
+ protected void onActivityResult(int requestCode, int resultCode,
201
+ @Nullable Intent data) {
202
+ super.onActivityResult(requestCode, resultCode, data);
203
+
204
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
205
+ if (!Settings.canDrawOverlays(this)) {
206
+ // User denied permission request, return permission error
207
+ if (mListener != null) {
208
+ mListener.onOpenFloatingWindowResult(ERR_INVALID_PARAMS, null);
209
+ }
210
+ } else {
211
+ // User granted permission
212
+ if (mListener != null) {
213
+ // Return success directly, let the caller retry
214
+ // startPictureInPicture
215
+ mListener.onOpenFloatingWindowResult(ERR_RETRY, null);
216
+ }
217
+ }
218
+ }
219
+
220
+ // Finish activity regardless of the result
221
+ finish();
222
+ }
223
+ }
224
+ }
@@ -0,0 +1,198 @@
1
+ package com.volcengine.velive.rn.pull.pictureInpicture;
2
+
3
+ import android.app.Service;
4
+ import android.content.Intent;
5
+ import android.graphics.PixelFormat;
6
+ import android.os.Build;
7
+ import android.os.IBinder;
8
+ import android.provider.Settings;
9
+ import android.util.DisplayMetrics; // Import DisplayMetrics
10
+ import android.util.Log;
11
+ import android.view.Gravity;
12
+ import android.view.LayoutInflater;
13
+ import android.view.MotionEvent;
14
+ import android.view.SurfaceView;
15
+ import android.view.View;
16
+ import android.view.ViewConfiguration;
17
+ import android.view.WindowManager;
18
+ import androidx.annotation.Nullable;
19
+ import com.volcengine.velive.rn.pull.R;
20
+
21
+ public class FloatingWindowService extends Service {
22
+ private static final String TAG = FloatingWindowService.class.getSimpleName();
23
+ private static final int LONGER_SIDE_MAX_LEN = 1000;
24
+
25
+ private WindowManager mWindowManager;
26
+ private WindowManager.LayoutParams mLayoutParams;
27
+ private SurfaceView mSurfaceView;
28
+ private View mSmallWindowView;
29
+
30
+ public static final String INTENT_EXTRA_KEY_ASPECT_RATIO = "aspect_ratio";
31
+ public static final String INTENT_EXTRA_KEY_X_POS = "x_pos";
32
+ public static final String INTENT_EXTRA_KEY_Y_POS = "y_pos";
33
+
34
+ @Override
35
+ public void onCreate() {
36
+ Log.d(TAG, "onCreate");
37
+ super.onCreate();
38
+ FloatingWindowHelper.getInstance().onStartService();
39
+ }
40
+
41
+ @Override
42
+ public int onStartCommand(Intent intent, int flags, int startId) {
43
+ Log.d(TAG, "onStartCommand");
44
+ initUI(intent.getFloatExtra(INTENT_EXTRA_KEY_ASPECT_RATIO, 16f / 9f),
45
+ intent.getIntExtra(INTENT_EXTRA_KEY_X_POS, 300),
46
+ intent.getIntExtra(INTENT_EXTRA_KEY_Y_POS, 300));
47
+ FloatingWindowHelper.getInstance().setSurfaceView(mSurfaceView);
48
+ return super.onStartCommand(intent, flags, startId);
49
+ }
50
+
51
+ @Override
52
+ public void onDestroy() {
53
+ Log.d(TAG, "onDestroy");
54
+ super.onDestroy();
55
+ if (mSmallWindowView != null) {
56
+ mWindowManager.removeView(mSmallWindowView);
57
+ }
58
+ FloatingWindowHelper.getInstance().onStopService();
59
+ }
60
+
61
+ @Nullable
62
+ @Override
63
+ public IBinder onBind(Intent intent) {
64
+ return null;
65
+ }
66
+
67
+ private void initUI(float aspectRatio, int x, int y) {
68
+ mWindowManager = (WindowManager)getSystemService(WINDOW_SERVICE);
69
+ mLayoutParams = new WindowManager.LayoutParams();
70
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
71
+ mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
72
+ } else {
73
+ mLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
74
+ }
75
+ mLayoutParams.format = PixelFormat.RGBA_8888;
76
+ mLayoutParams.gravity = Gravity.START | Gravity.TOP;
77
+ mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
78
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
79
+
80
+ // Get screen dimensions
81
+ DisplayMetrics displayMetrics = new DisplayMetrics();
82
+ mWindowManager.getDefaultDisplay().getMetrics(displayMetrics);
83
+ int screenWidth = displayMetrics.widthPixels;
84
+ int screenHeight = displayMetrics.heightPixels;
85
+
86
+ // Calculate the minimum screen dimension
87
+ int minScreenDim = Math.min(screenWidth, screenHeight);
88
+
89
+ // Determine the maximum length based on the smaller of 1000 and
90
+ // minScreenDim
91
+ int maxLen = Math.min(LONGER_SIDE_MAX_LEN, minScreenDim);
92
+
93
+ // Limit the floating window size to prevent it from being too large or too
94
+ // small, control the longer side to maxLen, scale the shorter
95
+ // side proportionally
96
+ int width, height;
97
+ if (aspectRatio >= 1) {
98
+ height = (int)(maxLen / aspectRatio);
99
+ width = maxLen;
100
+ } else {
101
+ width = (int)(maxLen * aspectRatio);
102
+ height = maxLen;
103
+ }
104
+ mLayoutParams.width = width;
105
+ mLayoutParams.height = height;
106
+
107
+ // Initial position of the floating window
108
+ mLayoutParams.x = x;
109
+ mLayoutParams.y = y;
110
+
111
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
112
+ if (Settings.canDrawOverlays(this)) {
113
+ LayoutInflater layoutInflater = LayoutInflater.from(this);
114
+ mSmallWindowView =
115
+ layoutInflater.inflate(R.layout.floating_window_layout, null);
116
+ mSmallWindowView.setOnTouchListener(new FloatingOnTouchListener());
117
+ mWindowManager.addView(mSmallWindowView, mLayoutParams);
118
+ mSurfaceView = mSmallWindowView.findViewById(R.id.surface_view);
119
+ mSmallWindowView.findViewById(R.id.surface_close_btn)
120
+ .setOnClickListener(v -> { stopSelf(); });
121
+
122
+ mSmallWindowView.findViewById(R.id.new_window_btn)
123
+ .setOnClickListener(v -> {
124
+ Log.d(TAG, "PIP window clicked");
125
+
126
+ try {
127
+ // Get the pacain activity
128
+ String packageName = this.getPackageName();
129
+ Intent launchIntent =
130
+ this.getPackageManager().getLaunchIntentForPackage(
131
+ packageName);
132
+
133
+ if (launchIntent != null) {
134
+ Log.d(TAG, "Launching app with intent: " + launchIntent);
135
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
136
+ this.startActivity(launchIntent);
137
+ } else {
138
+ Log.e(TAG, "Could not create launch intent for package: " +
139
+ packageName);
140
+ }
141
+ } catch (Exception e) {
142
+ Log.e(TAG, "Error launching app: " + e.getMessage(), e);
143
+ }
144
+
145
+ stopSelf();
146
+ });
147
+ }
148
+ }
149
+ }
150
+
151
+ private class FloatingOnTouchListener implements View.OnTouchListener {
152
+ private final int touchSlop =
153
+ ViewConfiguration
154
+ .get(FloatingWindowService.this.getApplicationContext())
155
+ .getScaledTouchSlop();
156
+ private int downX, downY;
157
+ private int x, y;
158
+ private boolean isDragging;
159
+
160
+ @Override
161
+ public boolean onTouch(View view, MotionEvent event) {
162
+ switch (event.getAction()) {
163
+ case MotionEvent.ACTION_UP:
164
+ if (isDragging) {
165
+ isDragging = false;
166
+ } else {
167
+ FloatingWindowHelper.getInstance().onClickFloatingWindow(
168
+ FloatingWindowService.this);
169
+ }
170
+ downX = downY = 0;
171
+ break;
172
+ case MotionEvent.ACTION_DOWN:
173
+ downX = x = (int)event.getRawX();
174
+ downY = y = (int)event.getRawY();
175
+ break;
176
+ case MotionEvent.ACTION_MOVE:
177
+ int nowX = (int)event.getRawX();
178
+ int nowY = (int)event.getRawY();
179
+ if (Math.abs(nowX - downX) > touchSlop ||
180
+ Math.abs(nowY - downY) > touchSlop) {
181
+ isDragging = true;
182
+ }
183
+ int movedX = nowX - x;
184
+ int movedY = nowY - y;
185
+ x = nowX;
186
+ y = nowY;
187
+ mLayoutParams.x = mLayoutParams.x + movedX;
188
+ mLayoutParams.y = mLayoutParams.y + movedY;
189
+ mWindowManager.updateViewLayout(view, mLayoutParams);
190
+ break;
191
+
192
+ default:
193
+ break;
194
+ }
195
+ return true;
196
+ }
197
+ }
198
+ }
@@ -0,0 +1,80 @@
1
+ package com.volcengine.velive.rn.pull.pictureInpicture;
2
+
3
+ import android.content.Context;
4
+ import android.view.SurfaceView;
5
+ import java.util.Map;
6
+
7
+ public interface IFloatingWindowHelper {
8
+ // Set event listener callback
9
+ void setEventListener(Listener listener);
10
+
11
+ // Open floating window (will request permission if not granted),
12
+ // also supports caching business data for restoring the live page.
13
+ void openFloatingWindow(Context context, Config config,
14
+ Map<String, Object> extraData);
15
+
16
+ // Close floating window
17
+ void closeFloatingWindow(Context context);
18
+
19
+ // Request overlay permission
20
+ void requestOverlayPermission(Context context);
21
+
22
+ // Get floating window configuration
23
+ Config getConfig();
24
+
25
+ // Get cached business data
26
+ Map<String, Object> getExtraData();
27
+
28
+ // Check if floating window is open
29
+ boolean isOpen();
30
+
31
+ interface Listener {
32
+ // Error codes for opening floating window callback
33
+ int ERR_NO = 0;
34
+ int ERR_NOT_SUPPORT = 1;
35
+ int ERR_INVALID_PARAMS = 2;
36
+ int ERR_IS_ALREADY_OPEN = 3;
37
+ int ERR_RETRY = 4;
38
+
39
+ // Triggered when requesting overlay permission
40
+ // return: boolean, whether to allow requesting overlay permission
41
+ // - true: allow
42
+ // - false: disallow
43
+ default boolean onRequestOverlayPermission() { return true; }
44
+
45
+ // Callback for the result of opening floating window
46
+ // param: errCode, ERR_NO means success, other values indicate errors
47
+ default void onOpenFloatingWindowResult(int errCode,
48
+ Map<String, Object> extraData) {}
49
+
50
+ // Callback when closing floating window
51
+ default void onCloseFloatingWindow(Map<String, Object> extraData) {}
52
+
53
+ // Callback when clicking the floating window, needs to be handled by
54
+ // business logic
55
+ default void onClickFloatingWindow(Context context) {}
56
+
57
+ // Callback when clicking the close button of floating window, needs to be
58
+ // handled by business logic
59
+ default void onClickFloatingWindowCloseBtn(Context context) {
60
+ FloatingWindowHelper.getInstance().closeFloatingWindow(context);
61
+ }
62
+
63
+ // Callback for the SurfaceView of floating window, needs to be set to SDK
64
+ // to render video
65
+ void onUpdateSurfaceView(SurfaceView surfaceView);
66
+ }
67
+
68
+ class Config {
69
+ public float aspectRatio;
70
+ int x, y; // Position for creating the floating window
71
+
72
+ public Config(float aspectRatio) { this(aspectRatio, 0, 0); }
73
+
74
+ public Config(float aspectRatio, int x, int y) {
75
+ this.aspectRatio = aspectRatio;
76
+ this.x = x;
77
+ this.y = y;
78
+ }
79
+ }
80
+ }