@@ -3,17 +3,20 @@ package video.api.reactnative.livestream
3
3
import android.Manifest
4
4
import android.annotation.SuppressLint
5
5
import android.content.Context
6
- import android.content.pm.PackageManager
7
6
import android.util.AttributeSet
8
7
import android.util.Log
9
8
import android.view.ScaleGestureDetector
10
9
import androidx.constraintlayout.widget.ConstraintLayout
11
- import androidx.core.app.ActivityCompat
10
+ import com.facebook.react.bridge.UiThreadUtil.runOnUiThread
11
+ import com.facebook.react.uimanager.ThemedReactContext
12
12
import video.api.livestream.ApiVideoLiveStream
13
13
import video.api.livestream.enums.CameraFacingDirection
14
- import video.api.livestream.interfaces.IConnectionChecker
14
+ import video.api.livestream.interfaces.IConnectionListener
15
15
import video.api.livestream.models.AudioConfig
16
16
import video.api.livestream.models.VideoConfig
17
+ import video.api.reactnative.livestream.utils.permissions.PermissionsManager
18
+ import video.api.reactnative.livestream.utils.permissions.SerialPermissionsManager
19
+ import video.api.reactnative.livestream.utils.showDialog
17
20
import java.io.Closeable
18
21
19
22
@@ -25,12 +28,20 @@ class LiveStreamView @JvmOverloads constructor(
25
28
) : ConstraintLayout(context, attrs, defStyle),
26
29
Closeable {
27
30
private val liveStream: ApiVideoLiveStream
31
+ private val permissionsManager = SerialPermissionsManager (
32
+ PermissionsManager ((context as ThemedReactContext ).reactApplicationContext)
33
+ )
28
34
35
+ // Connection listeners
29
36
var onConnectionSuccess: (() -> Unit )? = null
30
37
var onConnectionFailed: ((reason: String? ) -> Unit )? = null
31
38
var onDisconnected: (() -> Unit )? = null
32
39
33
- private val connectionListener = object : IConnectionChecker {
40
+ // Permission listeners
41
+ var onPermissionsDenied: ((List <String >) -> Unit )? = null
42
+ var onPermissionsRationale: ((List <String >) -> Unit )? = null
43
+
44
+ private val connectionListener = object : IConnectionListener {
34
45
override fun onConnectionSuccess () {
35
46
onConnectionSuccess?.let { it() }
36
47
}
@@ -48,8 +59,55 @@ class LiveStreamView @JvmOverloads constructor(
48
59
inflate(context, R .layout.react_native_livestream, this )
49
60
liveStream = ApiVideoLiveStream (
50
61
context = context,
51
- connectionChecker = connectionListener,
52
- apiVideoView = findViewById(R .id.apivideo_view)
62
+ connectionListener = connectionListener,
63
+ apiVideoView = findViewById(R .id.apivideo_view),
64
+ permissionRequester = { permissions, onGranted ->
65
+ permissionsManager.requestPermissions(
66
+ permissions,
67
+ onAllGranted = {
68
+ onGranted()
69
+ },
70
+ onShowPermissionRationale = { missingPermissions, onRequiredPermissionLastTime ->
71
+ runOnUiThread {
72
+ when {
73
+ missingPermissions.size > 1 -> {
74
+ context.showDialog(
75
+ R .string.permission_required,
76
+ R .string.camera_and_record_audio_permission_required_message,
77
+ android.R .string.ok,
78
+ onPositiveButtonClick = { onRequiredPermissionLastTime() }
79
+ )
80
+ }
81
+
82
+ missingPermissions.contains(Manifest .permission.CAMERA ) -> {
83
+ context.showDialog(
84
+ R .string.permission_required,
85
+ R .string.camera_permission_required_message,
86
+ android.R .string.ok,
87
+ onPositiveButtonClick = { onRequiredPermissionLastTime() }
88
+ )
89
+ }
90
+
91
+ missingPermissions.contains(Manifest .permission.RECORD_AUDIO ) -> {
92
+ context.showDialog(
93
+ R .string.permission_required,
94
+ R .string.record_audio_permission_required_message,
95
+ android.R .string.ok,
96
+ onPositiveButtonClick = { onRequiredPermissionLastTime() }
97
+ )
98
+ }
99
+ }
100
+ }
101
+ val permissionsStrings = missingPermissions.joinToString(" , " )
102
+ Log .e(TAG , " Asking rationale for missing permissions: $permissionsStrings " )
103
+ onPermissionsRationale?.let { it(missingPermissions) }
104
+ },
105
+ onAtLeastOnePermissionDenied = { missingPermissions ->
106
+ val permissionsStrings = missingPermissions.joinToString(" , " )
107
+ Log .e(TAG , " Missing permissions: $permissionsStrings " )
108
+ onPermissionsDenied?.let { it(missingPermissions) }
109
+ })
110
+ }
53
111
)
54
112
}
55
113
@@ -62,41 +120,59 @@ class LiveStreamView @JvmOverloads constructor(
62
120
var videoConfig: VideoConfig ?
63
121
get() = liveStream.videoConfig
64
122
set(value) {
65
- if (ActivityCompat .checkSelfPermission(
66
- context,
67
- Manifest .permission.CAMERA
68
- ) != PackageManager .PERMISSION_GRANTED
69
- ) {
70
- Log .e(TAG , " Missing permissions Manifest.permission.CAMERA" )
71
- throw UnsupportedOperationException (" Missing permissions Manifest.permission.CAMERA" )
72
- }
73
-
74
- liveStream.videoConfig = value
123
+ permissionsManager.requestPermission(
124
+ Manifest .permission.CAMERA ,
125
+ onGranted = {
126
+ liveStream.videoConfig = value
127
+ },
128
+ onShowPermissionRationale = { onRequiredPermissionLastTime ->
129
+ runOnUiThread {
130
+ context.showDialog(
131
+ R .string.permission_required,
132
+ R .string.camera_permission_required_message,
133
+ android.R .string.ok,
134
+ onPositiveButtonClick = { onRequiredPermissionLastTime() }
135
+ )
136
+ }
137
+ },
138
+ onDenied = {
139
+ Log .e(TAG , " Missing permissions Manifest.permission.CAMERA" )
140
+ onPermissionsDenied?.let { it(listOf (Manifest .permission.CAMERA )) }
141
+ })
75
142
}
76
143
77
144
78
145
var audioConfig: AudioConfig ?
79
146
get() = liveStream.audioConfig
80
147
set(value) {
81
- if (ActivityCompat .checkSelfPermission(
82
- context,
83
- Manifest .permission.RECORD_AUDIO
84
- ) != PackageManager .PERMISSION_GRANTED
85
- ) {
86
- Log .e(TAG , " Missing permissions Manifest.permission.RECORD_AUDIO" )
87
- throw UnsupportedOperationException (" Missing permissions Manifest.permission.RECORD_AUDIO" )
88
- }
89
-
90
- liveStream.audioConfig = value
148
+ permissionsManager.requestPermission(
149
+ Manifest .permission.RECORD_AUDIO ,
150
+ onGranted = {
151
+ liveStream.audioConfig = value
152
+ },
153
+ onShowPermissionRationale = { onRequiredPermissionLastTime ->
154
+ runOnUiThread {
155
+ context.showDialog(
156
+ R .string.permission_required,
157
+ R .string.record_audio_permission_required_message,
158
+ android.R .string.ok,
159
+ onPositiveButtonClick = { onRequiredPermissionLastTime() }
160
+ )
161
+ }
162
+ },
163
+ onDenied = {
164
+ Log .e(TAG , " Missing permissions Manifest.permission.RECORD_AUDIO" )
165
+ onPermissionsDenied?.let { it(listOf (Manifest .permission.RECORD_AUDIO )) }
166
+ })
91
167
}
92
168
93
169
val isStreaming: Boolean
94
170
get() = liveStream.isStreaming
95
171
96
172
var camera: CameraFacingDirection = CameraFacingDirection .BACK
97
- get() = liveStream.camera
173
+ get() = liveStream.cameraPosition
98
174
set(value) {
99
- liveStream.camera = value
175
+ liveStream.cameraPosition = value
100
176
field = value
101
177
}
102
178
@@ -148,6 +224,9 @@ class LiveStreamView @JvmOverloads constructor(
148
224
}
149
225
150
226
fun startStreaming (streamKey : String , url : String? ) {
227
+ require(permissionsManager.hasPermission(Manifest .permission.CAMERA )) { " Missing permissions Manifest.permission.CAMERA" }
228
+ require(permissionsManager.hasPermission(Manifest .permission.RECORD_AUDIO )) { " Missing permissions Manifest.permission.RECORD_AUDIO" }
229
+
151
230
url?.let { liveStream.startStreaming(streamKey, it) }
152
231
? : liveStream.startStreaming(streamKey)
153
232
}
0 commit comments