diff --git a/README.md b/README.md index dcdffa954..98b703172 100644 --- a/README.md +++ b/README.md @@ -19,171 +19,48 @@ Sample App of 100ms can be downloaded from Play store : https://play.google.com/ Meeting links can be generated using [dashboard](https://dashboard.100ms.live/) -## โ˜๏ธ Pre-requisites - -- Android Studio 3.0 or higher -- Support for Android API level 24 or higher -- Support for Java 8 -- This application uses build tool version `30.0.2` - -## ๐Ÿ“ฑ Supported Devices - -The Android SDK supports Android API level 21 and higher. It is built for armeabi-v7a, arm64-v8a, x86, and x86_64 architectures. - -## ๐Ÿš‚ Setup Guide - -- Clone this repository - -```bash -git clone --depth 1 https://github.com/100mslive/sample-app-android.git +## ๐Ÿ“ฆ Prebuilt (Room Kit) + +Room Kit is library built to take even building the UI off your hands. It uses 100ms SDK to and a dynamic layout decided by Prebuilt in the dashboard to have fully functional video up and running in your app in minutes. +To add the library to your own app, take a look at the [Prebuilt Quickstart](https://www.100ms.live/docs/android/v2/quickstart/prebuilt-android) in the docs. + +#### ๐Ÿ› ๏ธ Developing against Prebuilt Room Kit +We don't intend this to be a very common as Room Kit is updated very frequently with new features but the benefit of open source is that if you don't like how something works you can change it yourself! If you wanted to change anything in the Prebuilt library here's how. +1. Clone this repo. +2. Find the line in `app/build.gradle` that says `implementation "live.100ms:room-kit:$HMS_ROOM_KIT_VERSION"` and change it to + ``implementation project(":room-kit")`` +3. Run the app. + +Now you'll be loading the room kit library locally with your own changes rather than from the maven library that 100ms publishes to. + +To use this in your own code, you can put the library in your local computer and import it in your app. +To put the room kit library in your computer as a library locally: +1. Open a terminal and cd into the root of this project. Opening the terminal from Android Studio works as well. +2. Run the following command to build and put the library in your local storage. `./gradlew clean publishToMavenLocal` +3. Verify the library was built correctly by seeing if it is in `ls ~/.m2/repository/live/100ms/room-kit` +4. In your app find where the `mavenCentral()` repository is referenced and add `mavenLocal()` before it. Eg: +``` + allprojects { + repositories { + mavenLocal() + mavenCentral() + } + } ``` -- Get your token generation endpoint [by following this guide](https://docs.100ms.live/server-side/v2/foundation/authentication-and-tokens) - -- Create `app/gradle.properties` - - ```bash - cp app/example.gradle.properties app/gradle.properties - ``` - -- Put your endpoint URL as `TOKEN_ENDPOINT` in `app/gradle.properties`. Make sure it ends with a backslash (`/`) For example: - ```env - TOKEN_ENDPOINT="https://prod-in.100ms.live/hmsapi//" # Valid - TOKEN_ENDPOINT="https://prod-in.100ms.live/hmsapi/" # Invalid - ``` -- Create [firebase project](https://firebase.google.com/docs/android/setup#console) and save the [`google-services.json`](https://support.google.com/firebase/answer/7015592?hl=en) in `app/google-services.json` - -## ๐ŸŽ Run the application - -### ๐Ÿค– Run using Emulator - -Follow the official guide at [developers.android.com](https://developer.android.com/studio/run/emulator) to download and deploying app in a emulator. - -### ๐Ÿ“ฑ Run on Device (**recommended**) - -Follow the official guide at [developers.android.com](https://developer.android.com/studio/run/device) to setup your mobile device for development. - -On the first time of launch, user will be prompted with permissions. Then you are good to go to run the application. - -## ๐Ÿž Layout - -In the launch screen, here we have `Enter Name` and `Join Room`: - -We have many examples for each mode in the sample app using [`RecyclerView`](https://developer.android.com/reference/androidx/recyclerview/widget/RecyclerView), [`ViewPage2`](https://developer.android.com/jetpack/androidx/releases/viewpager2), [`LiveData](https://developer.android.com/topic/libraries/architecture/livedata) handling all data the right way with smooth updates. - -### ๐Ÿ“ž Join meeting - -- Paste the exact Room ID as obtained after signing up for the [100ms Dashboard] (https://dashboard.100ms.live/register) - -- Click `Join Now`. -- Video Conversation will be started ๐ŸŽ‰ +5. Add the import for `room kit` as you would do for any library `implementation "live.100ms:room-kit:$HMS_ROOM_KIT_VERSION"` - +That's it. Once you sync, the library should be available to your app. +This is the recommended method since adding the module to your own app directly would make it difficult to sync with our changes. -### Active Speaker Mode - -Shows the most actively speaking `Peer`'s in the room. Max number of peer's to be displayed can be configured be `Max Rows` and `Max Columns` setting in the Home Page. - -Each `Peer`'s VideoTile shows following updates in real-time: - -1. Blue border if speaking at that moment -2. Audio/Video track's mute/unmute status -3. Live Updates of `Peer`'s joining and leaving - -Related classes/files in this app: - 1. [`ActiveSpeakerFragment`](app/src/main/java/live/hms/app2/ui/meeting/activespeaker/ActiveSpeakerFragment.kt) - 2. [`ActiveSpeakerLRU`](app/src/main/java/live/hms/app2/ui/meeting/activespeaker/ActiveSpeakerLRU.kt) - 3. [`fragment_active_speaker.xml`](app/src/main/res/layout/fragment_active_speaker.xml) - 4. [`video_card.xml`](app/src/main/res/layout/video_card.xml) - -

- - -

- -### ๐ŸŽต Audio Only Mode - -Groups all `Peer`'s by their `Peer::role` and shows them using a nested `RecyclerView`: - -Each `Peer`'s AudioTile shows following updates in real-time: - -1. Blue border if speaking at that moment -2. Audio/Video track's mute/unmute status -3. Live Updates of `Peer`'s joining and leaving - -Related classes/files in this app: - 1. [`AudioCollection`](app/src/main/java/live/hms/app2/ui/meeting/audiomode/AudioCollection.kt) - 2. [`AudioCollectionAdapter`](app/src/main/java/live/hms/app2/ui/meeting/audiomode/AudioCollectionAdapter.kt) - 3. [`AudioCollectionDiffUtil`](app/src/main/java/live/hms/app2/ui/meeting/audiomode/AudioCollectionDiffUtil.kt) - 4. [`AudioItem`](app/src/main/java/live/hms/app2/ui/meeting/audiomode/AudioItem.kt) - 5. [`AudioItemsAdapter`](app/src/main/java/live/hms/app2/ui/meeting/audiomode/AudioItemsAdapter.kt) - 6. [`AudioItemsDiffUtil`](app/src/main/java/live/hms/app2/ui/meeting/audiomode/AudioItemsDiffUtil.kt) - 7. [`AudioModeFragment`](app/src/main/java/live/hms/app2/ui/meeting/audiomode/AudioModeFragment.kt) - 8. [`fragment_audio.xml`](app/src/main/res/layout/fragment_audio.xml) - 9. [`list_item_audio.xml`](app/src/main/res/layout/list_item_audio.xml) - 10. [`list_item_chat.xml`](app/src/main/res/layout/list_item_chat.xml) - -

- - -

- -### ๐Ÿ“š Grid View - -Show's all `Peer`'s in a 2x2 (default) Grid paginated using a `ViewPager`. - -Each `Peer`'s VideoTile shows following updates in real-time: - -1. Blue border if speaking at that moment -2. Audio/Video track's mute/unmute status -3. Live Updates of `Peer`'s joining and leaving -4. Subscribes/Unsubscribe videos which are not visible in the viewport -5. Handles updating the peer tracks status and peer-list smoothly without causing any re-renders - -Related classes/files in this app: - 1. [`VideoGridBaseFragment`](app/src/main/java/live/hms/app2/ui/meeting/commons/VideoGridBaseFragment.kt) - 2. [`VideoGridAdapter`](app/src/main/java/live/hms/app2/ui/meeting/videogrid/VideoGridAdapter.kt) - 3. [`VideoGridFragment`](app/src/main/java/live/hms/app2/ui/meeting/videogrid/VideoGridFragment.kt) - 4. [`VideoGridPageFragment`](app/src/main/java/live/hms/app2/ui/meeting/videogrid/VideoGridPageFragment.kt) - 5. [`VideoGridPageItem`](app/src/main/java/live/hms/app2/ui/meeting/videogrid/VideoGridPageItem.kt) - 6. [`VideoGridPageDiffUtil`](app/src/main/java/live/hms/app2/ui/meeting/videogrid/VideoGridPagerDiffUtil.kt) - 7. [`VideoGridPageViewModel`](app/src/main/java/live/hms/app2/ui/meeting/videogrid/VideoGridPageViewModel.kt) - 8. [`fragment_video_grid_page.xml`](app/src/main/res/layout/fragment_video_grid_page.xml) - 9. [`fragment_grid_video.xml`](app/src/main/res/layout/fragment_grid_video.xml) - 10. [`grid_item_video.xml`](app/src/main/res/layout/grid_item_video.xml) - - - -### ๐Ÿ™‹๐Ÿ™‹โ€โ™€๏ธ Hero View (experimental support ๐Ÿงช) - -Allows pinning any `Peer` video tile by clicking on respective `Peer`'s VideoTile from the -bottom tray. The bottom tray is implemented using a `RecyclerView` - -Related classes/files in this app: - 1. [`PinnedVideoFragment`](app/src/main/java/live/hms/app2/ui/meeting/pinnedvideo/PinnedVideoFragment.kt) - 2. [`VideoListAdapter`](app/src/main/java/live/hms/app2/ui/meeting/participants/VideoListAdapter.kt) - 3. [`VideoListItem`](app/src/main/java/live/hms/app2/ui/meeting/pinnedvideo/VideoListItem.kt) - 4. [`VideoListItemDiffUtil`](app/src/main/java/live/hms/app2/ui/meeting/pinnedvideo/VideoListItemDiffUtil.kt) - 5. [`fragment_pinned_video.xml`](app/src/main/res/layout/fragment_pinned_video.xml) - 6. [`fragment_pinned_video.xml`](app/src/main/res/layout-land/fragment_pinned_video.xml) - 7. [`list_item_video.xml`](app/src/main/res/layout/list_item_video.xml) - - +## โ˜๏ธ Pre-requisites -### โœ‹ Participant List View +- Android Studio +- Support for Java 11 -Shows a list of all `Peer`'s in the room. - 1. Entire list can be filter by `name` of the `Peer` - 2. Updates the list in real-time as `Peer`'s joins/leaves - 2. Show real-time status of `Peer` tracks and screen-share +## ๐Ÿ“ฑ Supported Devices -Related classes/files in this app: - 1. [`ParticipantsAdapter`](app/src/main/java/live/hms/app2/ui/meeting/participants/ParticipantsAdapter.kt) - 2. [`ParticipantsFragment`](app/src/main/java/live/hms/app2/ui/meeting/participants/ParticipantsFragment.kt) - 3. [`fragment_participants.xml`](app/src/main/res/layout/fragment_participants.xml) - 4. [`list_item_peer_list.xml`](app/src/main/res/layout/list_item_peer_list.xml) - - +The Android SDK supports Android API level 21 and higher. It is built for armeabi-v7a, arm64-v8a, x86, and x86_64 architectures. ## ๐Ÿ“‘ 100ms SDK Documentation diff --git a/gradle.properties b/gradle.properties index 9900b467e..d2190041a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,5 +22,5 @@ kotlin.code.style=official 100MS_APP_VERSION_CODE=643 100MS_APP_VERSION_NAME=5.8.872 hmsRoomKitGroup=live.100ms -HMS_ROOM_KIT_VERSION=1.1.6 +HMS_ROOM_KIT_VERSION=1.1.7 android.suppressUnsupportedCompileSdk=33 diff --git a/room-kit/build.gradle b/room-kit/build.gradle index b1293652d..d46772dbe 100644 --- a/room-kit/build.gradle +++ b/room-kit/build.gradle @@ -69,7 +69,7 @@ dependencies { implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.percentlayout:percentlayout:1.0.0' - def hmsVersion = "2.8.2" + def hmsVersion = "2.8.4" // To add dependencies of specific module implementation "live.100ms:android-sdk:$hmsVersion" implementation "live.100ms:video-view:$hmsVersion" diff --git a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/HlsVideoQualitySelectorBottomSheet.kt b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/HlsVideoQualitySelectorBottomSheet.kt index 483f5a9c9..4e94fa42a 100644 --- a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/HlsVideoQualitySelectorBottomSheet.kt +++ b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/HlsVideoQualitySelectorBottomSheet.kt @@ -1,5 +1,6 @@ package live.hms.roomkit.ui.meeting +import android.graphics.PorterDuff import android.os.Bundle import android.util.Log import android.view.LayoutInflater @@ -15,6 +16,9 @@ import live.hms.roomkit.databinding.DialogTrackSelectionBinding import live.hms.roomkit.util.viewLifecycle import live.hms.hls_player.HmsHlsPlayer import live.hms.hls_player.HmsHlsLayer +import live.hms.roomkit.ui.theme.HMSPrebuiltTheme +import live.hms.roomkit.ui.theme.getColorOrDefault +import live.hms.roomkit.ui.theme.trackTintList class HlsVideoQualitySelectorBottomSheet( @@ -34,6 +38,32 @@ class HlsVideoQualitySelectorBottomSheet( override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + binding.root.setBackgroundColor(getColorOrDefault( + HMSPrebuiltTheme.getColours()?.backgroundDefault, + HMSPrebuiltTheme.getDefaults().background_default)) + + binding.startConversationTv.setTextColor( + getColorOrDefault( + HMSPrebuiltTheme.getColours()?.onSurfaceHigh, + HMSPrebuiltTheme.getDefaults().onsurface_high_emp + ) + ) + + + binding.closeBtn.drawable.setTint( + getColorOrDefault( + HMSPrebuiltTheme.getColours()?.onSurfaceHigh, + HMSPrebuiltTheme.getDefaults().onsurface_high_emp + ) + ) + + binding.divider.setBackgroundColor( + getColorOrDefault( + HMSPrebuiltTheme.getColours()?.borderDefault, + HMSPrebuiltTheme.getDefaults().border_bright + ) + ) + dialog?.let { val sheet = it as BottomSheetDialog sheet.behavior.state = BottomSheetBehavior.STATE_EXPANDED @@ -51,7 +81,7 @@ class HlsVideoQualitySelectorBottomSheet( is HmsHlsLayer.LayerInfo -> { addTrackView("${layer.resolution.height}", index, currentLayer == layer) { hlsPlayer.setHmsHlsLayer(layer) - dismiss() + dismissAllowingStateLoss() } } } @@ -61,7 +91,7 @@ class HlsVideoQualitySelectorBottomSheet( private fun addAutoView(isSelected : Boolean) { addTrackView("Auto",-1,isSelected) { hlsPlayer.setHmsHlsLayer(HmsHlsLayer.AUTO) - dismiss() + dismissAllowingStateLoss() } } @@ -70,12 +100,26 @@ class HlsVideoQualitySelectorBottomSheet( val trackView = LayoutInflater.from(requireContext()) .inflate(R.layout.track_selection_view, null, false) - trackView.findViewById(R.id.track_label).text = "$title" - trackView.findViewById(R.id.track_radio_btn).isChecked = isSelected + trackView.findViewById(R.id.track_label).apply { + setTextColor( + getColorOrDefault( + HMSPrebuiltTheme.getColours()?.onSurfaceHigh, + HMSPrebuiltTheme.getDefaults().onsurface_high_emp + ) + ) + text = "$title" + } + + trackView.findViewById(R.id.track_radio_btn).apply { + buttonTintList = trackTintList() + isChecked = isSelected + } trackView.setOnClickListener { onSelectedListener.invoke(index) } + + binding.trackViewsParent.addView(trackView) } } \ No newline at end of file diff --git a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/MeetingFragment.kt b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/MeetingFragment.kt index b0ec7987a..5b129f3f1 100644 --- a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/MeetingFragment.kt +++ b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/MeetingFragment.kt @@ -25,7 +25,6 @@ import android.widget.RelativeLayout import android.widget.Toast import androidx.activity.OnBackPressedCallback import androidx.activity.result.contract.ActivityResultContracts -import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog import androidx.core.os.bundleOf import androidx.core.view.* @@ -179,7 +178,6 @@ class MeetingFragment : Fragment() { isCountdownManuallyCancelled = true hasStartedHls = false countDownTimer?.cancel() - unregisterPipActionListener() } override fun onPause() { @@ -197,8 +195,7 @@ class MeetingFragment : Fragment() { private fun updateActionVolumeMenuIcon( audioOutputType: HMSAudioManager.AudioDevice? = null ) { - binding.iconOutputDevice?.visibility = View.VISIBLE - binding.iconOutputDevice?.apply { + binding.iconOutputDevice.apply { when (audioOutputType) { HMSAudioManager.AudioDevice.EARPIECE -> { setIconEnabled(R.drawable.phone) @@ -399,6 +396,9 @@ class MeetingFragment : Fragment() { } // This only needs to be in meetingfragment since we always open it. // Is that true for HLS? Double check. + meetingViewModel.showAudioIcon.observe(viewLifecycleOwner) { visible -> + binding.iconOutputDevice.visibility = if(visible) View.VISIBLE else View.GONE + } meetingViewModel.initPrebuiltChatMessageRecipient.observe(viewLifecycleOwner) { chatViewModel.setInitialRecipient(it.first, it.second) ChatRbacRecipientHandling().updateChipRecipientUI(binding.sendToChipText, it.first) @@ -646,7 +646,7 @@ class MeetingFragment : Fragment() { meetingViewModel.isLocalAudioPresent.observe(viewLifecycleOwner) { allowed -> binding.buttonToggleAudio.visibility = if (allowed) View.VISIBLE else View.GONE //to show or hide mic icon [eg in HLS mode mic is not required] - updatePipMicState(allowed, true) + } meetingViewModel.isLocalVideoPresent.observe(viewLifecycleOwner) { allowed -> @@ -670,7 +670,6 @@ class MeetingFragment : Fragment() { meetingViewModel.isLocalAudioEnabled.observe(viewLifecycleOwner) { enabled -> //enable/disable mic on/off state - updatePipMicState(isMicOn = enabled) (binding.buttonToggleAudio as? ShapeableImageView)?.apply { if (enabled) { @@ -713,7 +712,6 @@ class MeetingFragment : Fragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) - registerPipActionListener() } private fun updatePipEndCall() { @@ -797,16 +795,15 @@ class MeetingFragment : Fragment() { if(modeEnteredOrExitedHls) { - val overlayIsVisible = isOverlayChatVisible() - if (meetingViewModel.prebuiltInfoContainer.isChatEnabled()) { - val isChatOverlay = meetingViewModel.prebuiltInfoContainer.isChatOverlay() - if (overlayIsVisible && !isChatOverlay) - toggleChatVisibility() - else if (!overlayIsVisible && isChatOverlay) - toggleChatVisibility() - } else if (overlayIsVisible) { - toggleChatVisibility() - } + val isChatEnabled = meetingViewModel.prebuiltInfoContainer.isChatEnabled() + val isChatOverlay = meetingViewModel.prebuiltInfoContainer.isChatOverlay() + val isChatOpenByDefault = meetingViewModel.prebuiltInfoContainer.chatInitialStateOpen() + val chatVisible = isChatEnabled && isChatOverlay && isChatOpenByDefault + toggleChatVisibility(chatVisible) + if(chatVisible) + moveChat(up = true, bottomMenuHeight = binding.bottomControls.height.toFloat()) + else + moveChat(up = false, binding.topMenu.height.toFloat()) } if(triggerFirstUpdate){ updateChatButtonWhenRoleChanges() @@ -933,7 +930,7 @@ class MeetingFragment : Fragment() { showSystemBars() // This prevents the bar from moving twice as high as it should if(shouldHideAfterDelay) - moveChat(up = true, bottomMenuHeight = binding.topMenu.height.toFloat()) + moveChat(up = true, bottomMenuHeight = binding.bottomControls.height.toFloat()) } override fun onAnimationEnd(animation: Animator) { @@ -989,13 +986,13 @@ class MeetingFragment : Fragment() { (layoutParams as RelativeLayout.LayoutParams).apply { removeRule(RelativeLayout.ALIGN_BOTTOM) addRule(RelativeLayout.ABOVE, R.id.bottom_controls) - updateMargins(bottom = bottomMenuHeight.toInt() + resources.getDimension(R.dimen.eight_dp).toInt()) + updateMargins(bottom = bottomMenuHeight.toInt() + 8.dp()) } } else { (layoutParams as RelativeLayout.LayoutParams).apply { removeRule(RelativeLayout.ABOVE) addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, R.id.meeting_container) - updateMargins(bottom = 8) + updateMargins(bottom = 8.dp()) } } } @@ -1011,7 +1008,7 @@ class MeetingFragment : Fragment() { ?.setListener(object : AnimatorListener { override fun onAnimationStart(animation: Animator) { topMenu.visibility = View.VISIBLE - moveChat(up = false, topMenu!!.height.toFloat()) + moveChat(up = false, bottomMenu.height.toFloat()) } override fun onAnimationEnd(animation: Animator) { @@ -1061,10 +1058,6 @@ class MeetingFragment : Fragment() { private fun hideProgressBar() { var isInPIPMode = false - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - if (activity?.isInPictureInPictureMode?.not() == true) - isInPIPMode = true - } binding.fragmentContainer.visibility = View.VISIBLE binding.bottomControls.visibility = View.VISIBLE if (!isInPIPMode && (meetingViewModel.meetingViewMode.value is MeetingViewMode.HLS_VIEWER).not()){ @@ -1220,7 +1213,6 @@ class MeetingFragment : Fragment() { binding.buttonEndCall.setOnSingleClickListener(350L) { requireActivity().onBackPressed() } - updatePipEndCall() binding.iconOutputDevice.apply { setOnSingleClickListener(200L) { @@ -1256,12 +1248,16 @@ class MeetingFragment : Fragment() { private fun isOverlayChatVisible() : Boolean { return binding.chatView.visibility == View.VISIBLE } - private fun toggleChatVisibility() { + private fun toggleChatVisibility(forceState : Boolean? = null) { with(binding.chatView) { - visibility = if (visibility == View.GONE) { - View.VISIBLE + visibility = if(forceState == null) { + if (visibility == View.GONE) { + View.VISIBLE + } else { + View.GONE + } } else { - View.GONE + if(forceState) View.VISIBLE else View.GONE } } binding.chatMessages.visibility = binding.chatView.visibility diff --git a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/MeetingViewModel.kt b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/MeetingViewModel.kt index 209531f10..820ea910d 100644 --- a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/MeetingViewModel.kt +++ b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/MeetingViewModel.kt @@ -83,6 +83,8 @@ class MeetingViewModel( val roleChange = MutableLiveData() private var numRoleChanges = 0 val roleChangeSingleShot : LiveData = roleChange.map { numRoleChanges++ } + var roleOnJoining : HMSRole? = null + private set fun isLargeRoom() = hmsRoom?.isLargeRoom?:false @@ -495,6 +497,7 @@ class MeetingViewModel( } override fun onPreview(room: HMSRoom, localTracks: Array) { + unMuteAllTracks(localTracks) previewUpdateData.postValue(Pair(room, localTracks)) } @@ -509,6 +512,15 @@ class MeetingViewModel( }) } + private fun unMuteAllTracks(localTracks: Array) { + localTracks.forEach { + if (it is HMSLocalVideoTrack) + it.setMute(false) + else if(it is HMSLocalAudioTrack) + it.setMute(false) + } + } + fun setLocalVideoEnabled(enabled: Boolean) { hmsSDK.getLocalPeer()?.videoTrack?.apply { @@ -724,6 +736,7 @@ class MeetingViewModel( Log.d(TAG, "Room name is ${room.name}") Log.d(TAG, "SessionId is: ${room.sessionId}") Log.d(TAG, "Room started at: ${room.startedAt}") + roleOnJoining = room.localPeer?.hmsRole // get the hls URL from the Room, if it exists val hlsUrl = room.hlsStreamingState.variants?.get(0)?.hlsStreamUrl @@ -811,6 +824,16 @@ class MeetingViewModel( "${hmsPeer.name} changed to ${hmsPeer.hmsRole.name}" ) if(hmsPeer.isLocal && type == HMSPeerUpdate.ROLE_CHANGED) { + // Changed on a force change. This will happen twice. + // when a person is brought to offstage + participantPreviousRoleChangeUseCase.setPreviousRole( + hmsSDK.getLocalPeer()!!, + roleOnJoining?.name, + object : HMSActionResultListener { + override fun onError(error: HMSException) {} + override fun onSuccess() {} + }) + initPrebuiltChatMessageRecipient.postValue(Pair(prebuiltInfoContainer.defaultRecipientToMessage(), ++recNum)) roleChange.postValue(hmsPeer) } @@ -820,6 +843,7 @@ class MeetingViewModel( updateThemeBasedOnCurrentRole(hmsPeer.hmsRole) val hlsUrl = hmsRoom?.hlsStreamingState?.variants?.get(0)?.hlsStreamUrl val isHlsPeer = isHlsPeer(hmsPeer.hmsRole) + showAudioIcon.postValue(!isHlsPeer) if (isHlsPeer) { switchToHlsViewIfRequired(hmsPeer.hmsRole, hlsUrl) } else { @@ -1096,6 +1120,7 @@ class MeetingViewModel( } override fun onTracks(localTracks: Array) { + unMuteAllTracks(localTracks) rolePreviewListener.onTracks(localTracks) } @@ -1194,10 +1219,12 @@ class MeetingViewModel( } } + val showAudioIcon : MutableLiveData = MutableLiveData(false) val showHlsStreamYetToStartError = MutableLiveData(false) private fun switchToHlsViewIfRequired(role: HMSRole?, streamUrl: String?) { var started = false val isHlsPeer = isHlsPeer(role) + showAudioIcon.postValue(!isHlsPeer) if (isHlsPeer && streamUrl != null) { started = true switchToHlsView(streamUrl) @@ -2208,9 +2235,25 @@ class MeetingViewModel( null } } + fun lowerRemotePeerHand(hmsPeer: HMSPeer, hmsActionResultListener: HMSActionResultListener) + = hmsSDK.lowerRemotePeerHand(hmsPeer, hmsActionResultListener) fun requestBringOnStage(handRaisePeer: HMSPeer, onStageRole: String) { - changeRole(handRaisePeer.peerID, onStageRole, false) + val force = prebuiltInfoContainer.shouldForceRoleChange() + changeRole(handRaisePeer.peerID, onStageRole, force) + + if(force) { + hmsSDK.lowerRemotePeerHand(handRaisePeer, object : HMSActionResultListener { + override fun onError(error: HMSException) { + Log.d(TAG,"Failed to lower peer's hand $error") + } + + override fun onSuccess() { + Log.d(TAG,"Lowered peer's hand since the role was force changed") + } + + }) + } } fun triggerErrorNotification(message: String, isDismissible: Boolean = true, type: HMSNotificationType = HMSNotificationType.Error, actionButtonText:String ="") { diff --git a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/PrebuiltInfoContainer.kt b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/PrebuiltInfoContainer.kt index 857250916..5d861f492 100644 --- a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/PrebuiltInfoContainer.kt +++ b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/PrebuiltInfoContainer.kt @@ -9,6 +9,7 @@ class PrebuiltInfoContainer(private val hmssdk: HMSSDK) { private val roleMap : MutableMap = mutableMapOf() private val localPeer by lazy { hmssdk.getLocalPeer()!! } + fun shouldForceRoleChange() : Boolean = onStageExp(localPeer.hmsRole.name)?.skipPreviewForRoleChange == true fun isAllowedToBlockUserFromChat() : Boolean = roleMap[localPeer.hmsRole.name]?.screens?.conferencing?.hlsLiveStreaming ?.elements?.chat?.realTimeControls?.canBlockUser == true || diff --git a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/activespeaker/HlsFragment.kt b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/activespeaker/HlsFragment.kt index 46a27f464..dba771f70 100644 --- a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/activespeaker/HlsFragment.kt +++ b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/activespeaker/HlsFragment.kt @@ -65,9 +65,6 @@ class HlsFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.applyTheme() - binding.btnSeekLive.setOnClickListener { - player.seekToLivePosition() - } meetingViewModel.showAudioMuted.observe(viewLifecycleOwner) { muted -> player.mute(muted) @@ -80,18 +77,33 @@ class HlsFragment : Fragment() { } binding.btnTrackSelection.setOnClickListener { - binding.hlsView.let { - val trackSelectionBottomSheet = HlsVideoQualitySelectorBottomSheet(player) - trackSelectionBottomSheet.show( - requireActivity().supportFragmentManager, - "trackSelectionBottomSheet" - ) + fadeinOutTrackSelectionButton() + if (binding.btnTrackSelection.alpha == 1f) { + binding.hlsView.let { + val trackSelectionBottomSheet = HlsVideoQualitySelectorBottomSheet(player) + trackSelectionBottomSheet.show( + requireActivity().supportFragmentManager, + "trackSelectionBottomSheet" + ) + } } } + binding.hlsView.setOnTouchListener { v, event -> + fadeinOutTrackSelectionButton() + false + } + + setPlayerStatsListener(true) } + private fun fadeinOutTrackSelectionButton() { + binding.btnTrackSelection.animate().cancel() + binding.btnTrackSelection.alpha = 1f + binding.btnTrackSelection.animate().alpha(0f).setStartDelay(3000).start() + } + private fun statsToString(playerStats: PlayerStatsModel): String { return "bitrate : ${Utils.humanReadableByteCount(playerStats.videoInfo.averageBitrate.toLong(),true,true)}/s \n" + "bufferedDuration : ${playerStats.bufferedDuration.absoluteValue/1000} s \n" + @@ -218,10 +230,6 @@ class HlsFragment : Fragment() { // It's live if the distance from the live edge is less than 10 seconds. val isLive = playerStats.distanceFromLive/1000 < SECONDS_FROM_LIVE // Show the button to go to live if it's not live. - binding.btnSeekLive.visibility = if(!isLive) - View.VISIBLE - else - View.GONE } override fun onStop() { diff --git a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/chat/ChatUseCase.kt b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/chat/ChatUseCase.kt index 130760d40..2dc44a552 100644 --- a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/chat/ChatUseCase.kt +++ b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/chat/ChatUseCase.kt @@ -34,7 +34,7 @@ class ChatUseCase { getAllowedRecipients : () -> AllowedToMessageParticipants?): ChatUiVisibilityState { return if(!isChatEnabled()) { ChatUiVisibilityState.DisabledFromLayout - } else if(isUserBlocked()/*chatViewModel.isUserBlockedFromChat()*/) { + } else if(isUserBlocked()) { ChatUiVisibilityState.Blocked } else { if(!chatPauseState.enabled) @@ -91,13 +91,13 @@ class ChatUseCase { fun updateState(externalChatPauseState: ChatPauseState? = null) { val overallChatState = getOverallChatState( - meetingViewModel, - chatViewModel, - isChatEnabled, - chatViewModel::isUserBlockedFromChat, - currentRbac, - externalChatPauseState ?: chatPauseState.value!!, - getAllowedRecipients + meetingViewModel = meetingViewModel, + chatViewModel = chatViewModel, + isChatEnabled = isChatEnabled, + isUserBlocked = chatViewModel::isUserBlockedFromChat, + getCurrentRecipient = currentRbac, + chatPauseState = externalChatPauseState ?: chatPauseState.value!!, + getAllowedRecipients = getAllowedRecipients ) diff --git a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/commons/VideoGridBaseFragment.kt b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/commons/VideoGridBaseFragment.kt index 85278acad..51393572a 100644 --- a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/commons/VideoGridBaseFragment.kt +++ b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/commons/VideoGridBaseFragment.kt @@ -490,25 +490,10 @@ abstract class VideoGridBaseFragment : Fragment() { * When onlyIndexToShow has a value it'll show the most active speaker only in pip mode */ private fun hideOrShowGridsForPip(onlyIndexToShow : Int? = null) { - var showAtleastOne = false - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && activity?.isInPictureInPictureMode == true && onlyIndexToShow != null && renderedViews.size > 0) { - renderedViews.forEachIndexed { index, renderedViewPair -> - if (onlyIndexToShow == index && renderedViewPair.binding.root.isVisible.not()) { - renderedViewPair.binding.root.visibility = View.VISIBLE - showAtleastOne = true - } else if (onlyIndexToShow != index && renderedViewPair.binding.root.isVisible) { - renderedViewPair.binding.root.visibility = View.GONE - } - } - if (showAtleastOne.not()) { - renderedViews[0].binding.root.visibility = View.VISIBLE - } - } else { renderedViews.forEachIndexed { index, renderedViewPair -> if (renderedViewPair.binding.root.isVisible.not()) renderedViewPair.binding.root.visibility = View.VISIBLE } - } } override fun onResume() { @@ -548,14 +533,9 @@ abstract class VideoGridBaseFragment : Fragment() { override fun onPause() { super.onPause() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && activity?.isInPictureInPictureMode == true) { - wasLastModePip = true - //force pip mode layout refresh - hideOrShowGridsForPip(wasLastSpeakingViewIndex) - } else { isFragmentVisible = false unbindViews() - } + } diff --git a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/participants/ParticipantItem.kt b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/participants/ParticipantItem.kt index 04cdbbdde..a35b281d3 100644 --- a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/participants/ParticipantItem.kt +++ b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/participants/ParticipantItem.kt @@ -17,6 +17,7 @@ import live.hms.roomkit.helpers.NetworkQualityHelper import live.hms.roomkit.show import live.hms.roomkit.ui.meeting.CustomPeerMetadata import live.hms.roomkit.ui.meeting.MeetingTrack +import live.hms.roomkit.ui.meeting.MeetingViewModel import live.hms.roomkit.ui.meeting.PrebuiltInfoContainer import live.hms.roomkit.ui.theme.HMSPrebuiltTheme import live.hms.roomkit.ui.theme.applyTheme @@ -44,7 +45,8 @@ class ParticipantItem( private val prebuiltInfoContainer: PrebuiltInfoContainer, private val participantPreviousRoleChangeUseCase: ParticipantPreviousRoleChangeUseCase, private val requestPeerLeave: (hmsPeer: HMSRemotePeer, reason: String) -> Unit, - private val activeSpeakers: LiveData, Array>> + private val activeSpeakers: LiveData, Array>>, + private val lowerRemotePeerHand : (HMSPeer, HMSActionResultListener) -> Unit ) : BindableItem(hmsPeer.peerID.hashCode().toLong()){ override fun bind(viewBinding: ListItemPeerListBinding, position: Int) { viewBinding.applyTheme() @@ -81,29 +83,36 @@ class ParticipantItem( if(bringOnStage) { popBinding.onStage.text = "Bring OnStage" popBinding.onStage.setOnClickListener { - participantPreviousRoleChangeUseCase.setPreviousRole(hmsPeer, object : - HMSActionResultListener { - override fun onError(error: HMSException) { - // Throw error - Log.d("BringOnStageError","$error") - } + val role = prebuiltInfoContainer.onStageExp(viewerPeer.hmsRole.name)?.onStageRole + if(role != null) { + val force = prebuiltInfoContainer.shouldForceRoleChange() + changeRole( + hmsPeer.peerID, + role, + force + ) + if(force) { + lowerRemotePeerHand(hmsPeer, object : HMSActionResultListener { + override fun onError(error: HMSException) { +// Log.d(TAG,"Failed to lower peer's hand $error") + } - override fun onSuccess() { - val role = prebuiltInfoContainer.onStageExp(viewerPeer.hmsRole.name)?.onStageRole - if(role != null) - changeRole(hmsPeer.peerID, role, false) - } - }) + override fun onSuccess() { +// Log.d(TAG,"Lowered peer's hand since the role was force changed") + } + + }) + } + } mypopupWindow.dismiss() } - } - if(bringOffStage){ + } else if(bringOffStage){ popBinding.onStage.text = "Remove From Stage" popBinding.onStage.setOnClickListener { val role = participantPreviousRoleChangeUseCase.getPreviousRole(hmsPeer) - Log.d("RolesChangingTo","$role") - if(role != null) - changeRole(hmsPeer.peerID, role, true) + if(role != null) { + changeRole(hmsPeer.peerID, role, true) + } mypopupWindow.dismiss() } } diff --git a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/participants/ParticipantPreviousRoleChangeUseCase.kt b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/participants/ParticipantPreviousRoleChangeUseCase.kt index 6c7584efd..e472ff89f 100644 --- a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/participants/ParticipantPreviousRoleChangeUseCase.kt +++ b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/participants/ParticipantPreviousRoleChangeUseCase.kt @@ -1,25 +1,29 @@ package live.hms.roomkit.ui.meeting.participants -import kotlinx.coroutines.CompletableDeferred import live.hms.roomkit.ui.meeting.CustomPeerMetadata -import live.hms.video.error.HMSException import live.hms.video.sdk.HMSActionResultListener -import live.hms.video.sdk.models.HMSLocalPeer import live.hms.video.sdk.models.HMSPeer class ParticipantPreviousRoleChangeUseCase(private val changeMetadata: (String, HMSActionResultListener) -> Unit) { fun getPreviousRole(peer: HMSPeer) : String? = CustomPeerMetadata.fromJson(peer.metadata)?.prevRole - fun setPreviousRole(peer : HMSPeer, hmsActionResultListener: HMSActionResultListener, toggleHandraise : Boolean =false) { + fun setPreviousRole( + peer: HMSPeer, + roleName: String?, + hmsActionResultListener: HMSActionResultListener + ) { val existingMetadata = CustomPeerMetadata.fromJson(peer.metadata) // Set the role or create a new metadata object with it. - val updatedMetadata = existingMetadata?.copy(prevRole = peer.hmsRole.name, isHandRaised = false) + val updatedMetadata = existingMetadata?.copy(prevRole = roleName, + name = peer.name, + isHandRaised = false, + isBRBOn = false) ?: CustomPeerMetadata( isHandRaised = false, isBRBOn = false, name = peer.name, - prevRole = peer.hmsRole.name + prevRole = roleName ) changeMetadata(updatedMetadata.toJson(), hmsActionResultListener) diff --git a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/participants/ParticipantsUseCase.kt b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/participants/ParticipantsUseCase.kt index 6b892db74..e7253651e 100644 --- a/room-kit/src/main/java/live/hms/roomkit/ui/meeting/participants/ParticipantsUseCase.kt +++ b/room-kit/src/main/java/live/hms/roomkit/ui/meeting/participants/ParticipantsUseCase.kt @@ -247,7 +247,8 @@ class ParticipantsUseCase(val meetingViewModel: MeetingViewModel, meetingViewModel.prebuiltInfoContainer, meetingViewModel.participantPreviousRoleChangeUseCase, meetingViewModel::requestPeerLeave, - meetingViewModel.activeSpeakers + meetingViewModel.activeSpeakers, + meetingViewModel::lowerRemotePeerHand ) }!!) } diff --git a/room-kit/src/main/java/live/hms/roomkit/ui/polls/PollsCreationFragment.kt b/room-kit/src/main/java/live/hms/roomkit/ui/polls/PollsCreationFragment.kt index a394236d3..8a44b401c 100644 --- a/room-kit/src/main/java/live/hms/roomkit/ui/polls/PollsCreationFragment.kt +++ b/room-kit/src/main/java/live/hms/roomkit/ui/polls/PollsCreationFragment.kt @@ -13,6 +13,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import kotlinx.coroutines.launch import live.hms.roomkit.R import live.hms.roomkit.databinding.LayoutPollsCreationBinding +import live.hms.roomkit.ui.meeting.MeetingState import live.hms.roomkit.ui.meeting.MeetingViewModel import live.hms.roomkit.ui.polls.display.PollDisplayFragment import live.hms.roomkit.ui.polls.previous.PreviousPollsAdaptor @@ -49,7 +50,7 @@ class PollsCreationFragment : Fragment(){ binding.creationFlowUi.visibility = if (meetingViewModel.isAllowedToCreatePolls()) View.VISIBLE else View.GONE with(binding) { applyTheme() - backButton.setOnSingleClickListener { findNavController().popBackStack() } + backButton.setOnSingleClickListener { closeFragment() } hideVoteCount.setOnCheckedChangeListener { _, isChecked -> pollsViewModel.markHideVoteCount(isChecked) } pollButton.setOnSingleClickListener { highlightPollOrQuiz(true) @@ -76,6 +77,16 @@ class PollsCreationFragment : Fragment(){ refreshPreviousPollsList() } + meetingViewModel.state.observe(viewLifecycleOwner) {state -> + when(state) { + is MeetingState.Failure, + is MeetingState.RoleChangeRequest, + is MeetingState.Disconnected, + is MeetingState.ForceLeave -> closeFragment() + else -> {} // nothing to do in other cases + } + } + lifecycleScope.launch { meetingViewModel.events.collect { event -> if(event is MeetingViewModel.Event.PollStarted || event is MeetingViewModel.Event.PollEnded) { @@ -87,6 +98,10 @@ class PollsCreationFragment : Fragment(){ } } + private fun closeFragment() { + findNavController().popBackStack() + } + private fun refreshPreviousPollsList() { val polls = meetingViewModel.hmsInteractivityCenterPolls() .sortedWith(compareBy({it.state},{-it.startedAt})) @@ -123,7 +138,7 @@ class PollsCreationFragment : Fragment(){ viewLifecycleOwner, object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { - binding.backButton.callOnClick() + closeFragment() } }) } diff --git a/room-kit/src/main/java/live/hms/roomkit/ui/role/RolePreviewFragment.kt b/room-kit/src/main/java/live/hms/roomkit/ui/role/RolePreviewFragment.kt index 40632959c..0d5cd6af5 100644 --- a/room-kit/src/main/java/live/hms/roomkit/ui/role/RolePreviewFragment.kt +++ b/room-kit/src/main/java/live/hms/roomkit/ui/role/RolePreviewFragment.kt @@ -98,31 +98,20 @@ class RolePreviewFragment : BottomSheetDialogFragment() { } binding.buttonJoinMeeting.setOnClickListener { - meetingViewModel.participantPreviousRoleChangeUseCase.setPreviousRole(meetingViewModel.hmsSDK.getLocalPeer()!!, - object : HMSActionResultListener { - override fun onError(error: HMSException) { - Log.e("RolePreviewFragment", "Error $error") - } + meetingViewModel.changeRoleAccept(onSuccess = { + contextSafe { context, activity -> + activity.runOnUiThread { + binding.previewView.removeTrack() + meetingViewModel.lowerLocalPeerHand() + findNavController().navigate( + RolePreviewFragmentDirections.actionRolePreviewFragmentToMeetingFragment( + false + ) + ) - override fun onSuccess() { - meetingViewModel.changeRoleAccept(onSuccess = { - contextSafe { context, activity -> - activity.runOnUiThread { - binding.previewView.removeTrack() - meetingViewModel.lowerLocalPeerHand() - findNavController().navigate( - RolePreviewFragmentDirections.actionRolePreviewFragmentToMeetingFragment( - false - ) - ) - - } - } - }) } - - }, toggleHandraise = true) - + } + }) } binding.declineButton.setOnClickListener { diff --git a/room-kit/src/main/java/live/hms/roomkit/ui/settings/SettingsStore.kt b/room-kit/src/main/java/live/hms/roomkit/ui/settings/SettingsStore.kt index 76ce48d97..fd2a86bf0 100644 --- a/room-kit/src/main/java/live/hms/roomkit/ui/settings/SettingsStore.kt +++ b/room-kit/src/main/java/live/hms/roomkit/ui/settings/SettingsStore.kt @@ -44,8 +44,8 @@ class SettingsStore(context: Context) { const val LEAK_CANARY = "leak-canary" const val SHOW_RECONNECTING_PROGRESS_BARS = "show-reconnecting-progress-bar" const val SHOW_PREVIEW_BEFORE_JOIN = "show-preview-before-join" - const val AUDIO_TRACK_INIT_STATE = "audio-track-init-state" - const val VIDEO_TRACK_INIT_STATE = "video-track-init-state" + const val AUDIO_TRACK_INIT_STATE = "audio-track-init-state-prebuilt" + const val VIDEO_TRACK_INIT_STATE = "video-track-init-state-prebuilt" const val SET_INITIAL_NAME_FROM_CLIENT = "set-initial-name-from-client" const val RTMP_URL_LIST = "rtmp-url-list" const val USE_HARDWARE_AEC = "use-hardware-aec" @@ -205,11 +205,11 @@ class SettingsStore(context: Context) { set(value) = putLong(AUDIO_POLL_INTERVAL, value) var isAudioTrackInitStateEnabled: Boolean - get() = sharedPreferences.getBoolean(AUDIO_TRACK_INIT_STATE, true) + get() = sharedPreferences.getBoolean(AUDIO_TRACK_INIT_STATE, false) set(value) = putBoolean(AUDIO_TRACK_INIT_STATE, value) var isVideoTrackInitStateEnabled: Boolean - get() = sharedPreferences.getBoolean(VIDEO_TRACK_INIT_STATE, true) + get() = sharedPreferences.getBoolean(VIDEO_TRACK_INIT_STATE, false) set(value) = putBoolean(VIDEO_TRACK_INIT_STATE, value) var silenceAudioLevelThreshold: Int diff --git a/room-kit/src/main/java/live/hms/roomkit/ui/theme/ThemeExt.kt b/room-kit/src/main/java/live/hms/roomkit/ui/theme/ThemeExt.kt index 203c0b4c4..96c02ce20 100644 --- a/room-kit/src/main/java/live/hms/roomkit/ui/theme/ThemeExt.kt +++ b/room-kit/src/main/java/live/hms/roomkit/ui/theme/ThemeExt.kt @@ -1756,7 +1756,9 @@ internal fun ListItemChatBinding.applyTheme() { internal fun HlsFragmentLayoutBinding.applyTheme() { - + btnTrackSelection.drawable?.setTint(getColorOrDefault( + HMSPrebuiltTheme.getColours()?.onSurfaceMedium, + HMSPrebuiltTheme.getDefaults().onsurface_med_emp)) progressBar.progressTintList = ColorStateList.valueOf(getColorOrDefault( HMSPrebuiltTheme.getColours()?.primaryDefault, HMSPrebuiltTheme.getDefaults().onsurface_high_emp)) @@ -1971,7 +1973,7 @@ fun CustomMenuLayoutBinding.applyTheme(options : EnabledMenuOptions) { } } -private fun trackTintList() : ColorStateList { + fun trackTintList() : ColorStateList { val checkedUncheckedState = arrayOf(intArrayOf(android.R.attr.state_checked), intArrayOf(-android.R.attr.state_checked)) diff --git a/room-kit/src/main/res/drawable/settings.xml b/room-kit/src/main/res/drawable/settings.xml new file mode 100644 index 000000000..79abc15d2 --- /dev/null +++ b/room-kit/src/main/res/drawable/settings.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + diff --git a/room-kit/src/main/res/layout/dialog_track_selection.xml b/room-kit/src/main/res/layout/dialog_track_selection.xml index ec4564970..0d03ceadc 100644 --- a/room-kit/src/main/res/layout/dialog_track_selection.xml +++ b/room-kit/src/main/res/layout/dialog_track_selection.xml @@ -8,49 +8,60 @@ android:fitsSystemWindows="true" android:orientation="vertical" android:paddingHorizontal="16dp" - android:paddingVertical="20dp" + android:paddingVertical="16dp" tools:context=".ui.meeting.MeetingActivity"> - + android:id="@+id/headerFrame" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + + + + + - + app:layout_constraintTop_toBottomOf="@+id/headerFrame" /> + app:layout_constraintTop_toTopOf="@+id/edit_container_name" + app:layout_constraintVertical_bias="0.0"> + app:layout_constraintTop_toTopOf="@+id/edit_container_name" + app:layout_constraintVertical_bias="0.0" /> diff --git a/room-kit/src/main/res/layout/hls_fragment_layout.xml b/room-kit/src/main/res/layout/hls_fragment_layout.xml index 6011ee366..fae0332c0 100644 --- a/room-kit/src/main/res/layout/hls_fragment_layout.xml +++ b/room-kit/src/main/res/layout/hls_fragment_layout.xml @@ -23,7 +23,6 @@ - - - - - + diff --git a/room-kit/src/main/res/layout/track_selection_view.xml b/room-kit/src/main/res/layout/track_selection_view.xml index 794ee806b..2ccbf8dd4 100644 --- a/room-kit/src/main/res/layout/track_selection_view.xml +++ b/room-kit/src/main/res/layout/track_selection_view.xml @@ -1,13 +1,13 @@ - + android:fontFamily="@font/inter_regular" + android:text="360p" + android:textSize="13sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/track_radio_btn" + app:layout_constraintHorizontal_bias="0.0" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> - +