Skip to content

Commit d9f28cd

Browse files
xanderzhao-zbAbySwifter
authored andcommitted
【TUILiveKit】Supports in-app picture-in-picture
1 parent 09a487c commit d9f28cd

File tree

22 files changed

+758
-517
lines changed

22 files changed

+758
-517
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.DS_Store
2+
**/.DS_Store
Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,33 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
33

4+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
5+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
6+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
7+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
48

5-
<uses-permission android:name="android.permission.RECORD_AUDIO" />
6-
<uses-permission android:name="android.permission.INTERNET" />
7-
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
8-
<uses-permission android:name="android.permission.CAMERA" />
9-
<uses-permission
10-
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
11-
android:maxSdkVersion="28" />
12-
<uses-permission
13-
android:name="android.permission.READ_EXTERNAL_STORAGE"
14-
android:maxSdkVersion="32" />
15-
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
16-
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
17-
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
9+
<application>
10+
<activity
11+
android:name="io.trtc.tuikit.atomicx.common.permission.PermissionActivity"
12+
android:configChanges="orientation|keyboardHidden|screenSize"
13+
android:launchMode="singleTask"
14+
android:theme="@style/CommonActivityTranslucent"
15+
android:windowSoftInputMode="stateHidden|stateAlwaysHidden" />
1816

19-
</manifest>
17+
<service
18+
android:name="io.trtc.tuikit.atomicx.common.foregroundservice.AudioForegroundService"
19+
android:exported="false"
20+
android:foregroundServiceType="microphone" />
21+
22+
<service
23+
android:name="io.trtc.tuikit.atomicx.common.foregroundservice.VideoForegroundService"
24+
android:exported="false"
25+
android:foregroundServiceType="camera|microphone" />
26+
27+
<service
28+
android:name="io.trtc.tuikit.atomicx.common.foregroundservice.MediaForegroundService"
29+
android:enabled="true"
30+
android:exported="false"
31+
android:foregroundServiceType="mediaPlayback" />
32+
</application>
33+
</manifest>

live/tuilivekit/src/main/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
android:launchMode="singleTask"
3333
android:screenOrientation="portrait"
3434
android:supportsPictureInPicture="true"
35+
android:taskAffinity="com.trtc.uikit.livekit.pip.live.stream"
3536
android:theme="@style/Theme.AppCompat.NoActionBar" />
3637

3738
<activity
@@ -48,6 +49,7 @@
4849
android:screenOrientation="portrait"
4950
android:supportsPictureInPicture="true"
5051
android:theme="@style/Theme.AppCompat.NoActionBar"
52+
android:taskAffinity="com.trtc.uikit.livekit.pip.live.stream"
5153
android:windowSoftInputMode="adjustNothing" />
5254

5355
<activity

live/tuilivekit/src/main/java/com/trtc/uikit/livekit/common/PermissionRequest.kt

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,29 @@ package com.trtc.uikit.livekit.common
22

33
import android.Manifest
44
import android.content.Context
5-
import com.trtc.tuikit.common.permission.PermissionCallback
6-
import com.trtc.tuikit.common.permission.PermissionRequester
75
import com.trtc.uikit.livekit.R
6+
import io.trtc.tuikit.atomicx.common.permission.PermissionCallback
7+
import io.trtc.tuikit.atomicx.common.permission.PermissionRequester
8+
import kotlinx.coroutines.CoroutineScope
9+
import kotlinx.coroutines.Dispatchers
10+
import kotlinx.coroutines.channels.BufferOverflow
11+
import kotlinx.coroutines.flow.MutableSharedFlow
12+
import kotlinx.coroutines.launch
813

914
object PermissionRequest {
10-
fun requestMicrophonePermissions(context: Context, callback: PermissionCallback?) {
15+
val requestCompleteEvent = MutableSharedFlow<Boolean>(
16+
replay = 0,
17+
extraBufferCapacity = 0,
18+
onBufferOverflow = BufferOverflow.SUSPEND
19+
)
20+
21+
fun sendRequestCompleteEvent() {
22+
CoroutineScope(Dispatchers.Default).launch {
23+
requestCompleteEvent.emit(true)
24+
}
25+
}
26+
27+
fun requestMicrophonePermissions(context: Context, callback: PermissionCallback) {
1128
val title = context.getString(R.string.common_permission_microphone)
1229
val reason = context.getString(R.string.common_permission_mic_reason)
1330

@@ -33,7 +50,7 @@ object PermissionRequest {
3350
.request()
3451
}
3552

36-
fun requestCameraPermissions(context: Context, callback: PermissionCallback?) {
53+
fun requestCameraPermissions(context: Context, callback: PermissionCallback) {
3754
val title = context.getString(R.string.common_permission_camera)
3855
val reason = context.getString(R.string.common_permission_camera_reason)
3956

live/tuilivekit/src/main/java/com/trtc/uikit/livekit/features/anchorboardcast/AnchorView.kt

Lines changed: 65 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import android.content.Intent
88
import android.os.Build
99
import android.text.TextUtils
1010
import android.util.AttributeSet
11-
import android.util.Log
1211
import android.view.LayoutInflater
1312
import android.view.View
1413
import android.view.ViewGroup
@@ -19,10 +18,6 @@ import com.tencent.cloud.tuikit.engine.extension.TUILiveListManager
1918
import com.tencent.qcloud.tuicore.TUIConstants
2019
import com.tencent.qcloud.tuicore.TUICore
2120
import com.tencent.qcloud.tuicore.TUIThemeManager
22-
import com.trtc.tuikit.common.foregroundservice.VideoForegroundService
23-
import com.trtc.tuikit.common.permission.PermissionCallback
24-
import com.trtc.tuikit.common.system.ContextProvider
25-
import com.trtc.tuikit.common.util.ScreenUtil
2621
import com.trtc.uikit.livekit.R
2722
import com.trtc.uikit.livekit.common.COMPONENT_LIVE_STREAM
2823
import com.trtc.uikit.livekit.common.ErrorLocalized
@@ -71,6 +66,11 @@ import com.trtc.uikit.livekit.features.anchorboardcast.view.cohost.widgets.CoHos
7166
import com.trtc.uikit.livekit.features.anchorboardcast.view.cohost.widgets.CoHostForegroundWidgetsView
7267
import com.trtc.uikit.livekit.features.anchorboardcast.view.settings.SettingsPanelDialog
7368
import com.trtc.uikit.livekit.features.anchorboardcast.view.usermanage.UserManagerDialog
69+
import io.trtc.tuikit.atomicx.common.foregroundservice.VideoForegroundService
70+
import io.trtc.tuikit.atomicx.common.permission.PermissionCallback
71+
import com.tencent.cloud.tuikit.engine.common.ContextProvider
72+
import io.trtc.tuikit.atomicx.common.util.ScreenUtil
73+
import io.trtc.tuikit.atomicx.common.util.ScreenUtil.dip2px
7474
import io.trtc.tuikit.atomicx.widget.basicwidget.alertdialog.AtomicAlertDialog
7575
import io.trtc.tuikit.atomicx.widget.basicwidget.alertdialog.addItem
7676
import io.trtc.tuikit.atomicx.widget.basicwidget.alertdialog.cancelButton
@@ -108,7 +108,6 @@ import io.trtc.tuikit.atomicxcore.api.live.SeatLayoutTemplate
108108
import io.trtc.tuikit.atomicxcore.api.live.SeatUserInfo
109109
import io.trtc.tuikit.atomicxcore.api.live.StopLiveCompletionHandler
110110
import io.trtc.tuikit.atomicxcore.api.live.TakeSeatMode
111-
import io.trtc.tuikit.atomicxcore.api.live.deprecated.LiveCoreViewDeprecated
112111
import io.trtc.tuikit.atomicxcore.api.login.LoginStore
113112
import io.trtc.tuikit.atomicxcore.api.view.CoreViewType
114113
import io.trtc.tuikit.atomicxcore.api.view.LiveCoreView
@@ -118,8 +117,6 @@ import kotlinx.coroutines.CoroutineScope
118117
import kotlinx.coroutines.Dispatchers
119118
import kotlinx.coroutines.Job
120119
import kotlinx.coroutines.launch
121-
import org.json.JSONException
122-
import org.json.JSONObject
123120
import java.util.Locale
124121
import java.util.Objects
125122

@@ -236,12 +233,13 @@ class AnchorView @JvmOverloads constructor(
236233
override fun onHostInvitationNoResponse(guestUser: LiveUserInfo, reason: NoResponseReason) {
237234
if (reason == NoResponseReason.TIMEOUT) {
238235
logger.info("${hashCode()} onUserConnectionAccepted:[guestUser:$guestUser]")
239-
val context = ContextProvider.getApplicationContext()
240-
AtomicToast.show(
241-
context,
242-
context.resources.getString(R.string.common_voiceroom_take_seat_timeout),
243-
AtomicToast.Style.INFO
244-
)
236+
ContextProvider.getApplicationContext()?.apply {
237+
AtomicToast.show(
238+
context,
239+
context.resources.getString(R.string.common_voiceroom_take_seat_timeout),
240+
AtomicToast.Style.INFO
241+
)
242+
}
245243
}
246244
}
247245
}
@@ -546,8 +544,6 @@ class AnchorView @JvmOverloads constructor(
546544
}
547545
})
548546

549-
PIPPanelStore.sharedInstance().state.isAnchorStreaming = true
550-
551547
if (behavior == RoomBehavior.ENTER_ROOM) {
552548
enterRoom()
553549
} else {
@@ -563,6 +559,7 @@ class AnchorView @JvmOverloads constructor(
563559
val liveListStore = LiveListStore.shared()
564560
liveListStore.joinLive(it.roomId, object : LiveInfoCompletionHandler {
565561
override fun onSuccess(liveInfo: LiveInfo) {
562+
setCoreViewLayoutParamsWhenLandscape(liveInfo.seatTemplate)
566563
startLocalPreview(liveInfo)
567564
val activity = baseContext as Activity
568565
if (activity.isFinishing || activity.isDestroyed) {
@@ -634,37 +631,39 @@ class AnchorView @JvmOverloads constructor(
634631

635632
private fun startLocalPreview(liveInfo: LiveInfo) {
636633
if (liveInfo.keepOwnerOnSeat) {
637-
PermissionRequest.requestCameraPermissions(
638-
ContextProvider.getApplicationContext(), object : PermissionCallback() {
639-
override fun onGranted() {
640-
logger.info("requestCameraPermissions:[onGranted]")
641-
DeviceStore.shared().openLocalCamera(true, object : CompletionHandler {
642-
override fun onSuccess() {
643-
logger.info("startCamera success, requestMicrophonePermissions")
644-
PermissionRequest.requestMicrophonePermissions(
645-
ContextProvider.getApplicationContext(), object : PermissionCallback() {
646-
override fun onGranted() {
647-
logger.info("requestMicrophonePermissions success")
648-
DeviceStore.shared().openLocalMicrophone(null)
649-
}
650-
651-
override fun onDenied() {
652-
logger.error("requestMicrophonePermissions:[onDenied]")
653-
}
654-
})
655-
}
656-
657-
override fun onFailure(code: Int, desc: String) {
658-
logger.error("startCamera failed:code:$code,desc:$desc")
659-
}
660-
661-
})
662-
}
634+
ContextProvider.getApplicationContext()?.apply {
635+
PermissionRequest.requestCameraPermissions(
636+
this, object : PermissionCallback() {
637+
override fun onGranted() {
638+
logger.info("requestCameraPermissions:[onGranted]")
639+
DeviceStore.shared().openLocalCamera(true, object : CompletionHandler {
640+
override fun onSuccess() {
641+
logger.info("startCamera success, requestMicrophonePermissions")
642+
PermissionRequest.requestMicrophonePermissions(
643+
this@apply, object : PermissionCallback() {
644+
override fun onGranted() {
645+
logger.info("requestMicrophonePermissions success")
646+
DeviceStore.shared().openLocalMicrophone(null)
647+
}
648+
649+
override fun onDenied() {
650+
logger.error("requestMicrophonePermissions:[onDenied]")
651+
}
652+
})
653+
}
654+
655+
override fun onFailure(code: Int, desc: String) {
656+
logger.error("startCamera failed:code:$code,desc:$desc")
657+
}
658+
659+
})
660+
}
663661

664-
override fun onDenied() {
665-
logger.error("requestCameraPermissions:[onDenied]")
666-
}
667-
})
662+
override fun onDenied() {
663+
logger.error("requestCameraPermissions:[onDenied]")
664+
}
665+
})
666+
}
668667
}
669668
}
670669

@@ -751,6 +750,17 @@ class AnchorView @JvmOverloads constructor(
751750
}
752751
}
753752

753+
private fun setCoreViewLayoutParamsWhenLandscape(template: SeatLayoutTemplate) {
754+
logger.info("setCoreViewLayoutParamsWhenLandscape:template:$template,")
755+
if (template == SeatLayoutTemplate.VideoLandscape4Seats) {
756+
layoutCoreViewContainer.setRadius(0)
757+
val layoutParams: FrameLayout.LayoutParams = layoutCoreViewContainer.layoutParams as FrameLayout.LayoutParams
758+
layoutParams.topMargin = dip2px(150f)
759+
layoutParams.height = ScreenUtil.getScreenWidth(context) * 720 / 1280
760+
layoutCoreViewContainer.layoutParams = layoutParams
761+
}
762+
}
763+
754764
private fun initBarrageStreamView() {
755765
val ownerUserId = LiveListStore.shared().liveState.currentLive.value.liveOwner.userID
756766
val liveId = LiveListStore.shared().liveState.currentLive.value.liveID
@@ -1393,18 +1403,18 @@ class AnchorView @JvmOverloads constructor(
13931403
}
13941404

13951405
private fun startForegroundService() {
1396-
val context = ContextProvider.getApplicationContext()
1397-
VideoForegroundService.start(
1398-
context,
1399-
context.getString(context.applicationInfo.labelRes),
1400-
context.getString(R.string.common_app_running),
1401-
0
1402-
)
1406+
ContextProvider.getApplicationContext()?.apply {
1407+
VideoForegroundService.start(
1408+
context,
1409+
context.getString(context.applicationInfo.labelRes),
1410+
context.getString(R.string.common_app_running),
1411+
0
1412+
)
1413+
}
14031414
}
14041415

14051416
private fun stopForegroundService() {
1406-
val context = ContextProvider.getApplicationContext()
1407-
VideoForegroundService.stop(context)
1417+
ContextProvider.getApplicationContext()?.apply { VideoForegroundService.stop(context) }
14081418
}
14091419

14101420
private fun onCoGuestApplicantsChange(applicants: List<LiveUserInfo>) {

live/tuilivekit/src/main/java/com/trtc/uikit/livekit/features/anchorboardcast/view/coguest/panel/AnchorManagerDialog.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import android.widget.TextView
1010
import com.tencent.cloud.tuikit.engine.room.TUIRoomDefine
1111
import com.tencent.cloud.tuikit.engine.room.TUIRoomEngine
1212
import com.tencent.cloud.tuikit.engine.room.TUIRoomObserver
13-
import com.trtc.tuikit.common.permission.PermissionCallback
1413
import com.trtc.tuikit.common.system.ContextProvider
1514
import com.trtc.uikit.livekit.R
1615
import com.trtc.uikit.livekit.common.ErrorLocalized
@@ -19,6 +18,7 @@ import com.trtc.uikit.livekit.common.PermissionRequest
1918
import com.trtc.uikit.livekit.common.completionHandler
2019
import com.trtc.uikit.livekit.features.anchorboardcast.store.AnchorStore
2120
import com.trtc.uikit.livekit.features.anchorboardcast.store.MediaStore
21+
import io.trtc.tuikit.atomicx.common.permission.PermissionCallback
2222
import io.trtc.tuikit.atomicx.widget.basicwidget.alertdialog.AtomicAlertDialog
2323
import io.trtc.tuikit.atomicx.widget.basicwidget.alertdialog.cancelButton
2424
import io.trtc.tuikit.atomicx.widget.basicwidget.alertdialog.confirmButton

live/tuilivekit/src/main/java/com/trtc/uikit/livekit/features/anchorboardcast/view/settings/SettingsListAdapter.kt

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,6 @@ class SettingsListAdapter(
8686
ITEM_TYPE_DASHBOARD
8787
)
8888
)
89-
90-
data.add(
91-
SettingsItem(
92-
context.getString(R.string.common_video_settings_item_pip),
93-
R.drawable.livekit_pip_icon,
94-
ITEM_TYPE_PIP
95-
)
96-
)
9789
}
9890

9991
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {

0 commit comments

Comments
 (0)