Skip to content

Commit 03f6db2

Browse files
authored
Override default ExoPlayer layouts with custom ones to prevent overriding by other versions of the ExoPlayer library (XML). (#6013)
1 parent 8a8f0c4 commit 03f6db2

File tree

9 files changed

+518
-12
lines changed

9 files changed

+518
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161

6262
## stream-chat-android-ui-components
6363
### 🐞 Fixed
64+
- Fix ExoPlayer crash when playing a video, happening when the integration app is using the legacy `com.google.android.exoplayer` library. [#6013](https://github.com/GetStream/stream-chat-android/pull/6013)
6465

6566
### ⬆️ Improved
6667

stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/gallery/AttachmentMediaActivity.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,14 @@ public class AttachmentMediaActivity : AppCompatActivity() {
147147
binding.controls.show()
148148
}
149149
}
150-
controllerHideOnTouch = true
150+
setControllerHideOnTouch(true)
151151
setShowBuffering(PlayerView.SHOW_BUFFERING_NEVER)
152-
artworkDisplayMode = PlayerView.ARTWORK_DISPLAY_MODE_OFF
152+
setArtworkDisplayMode(PlayerView.ARTWORK_DISPLAY_MODE_OFF)
153+
// Disable default controller because we use the legacy one
154+
setUseController(false)
153155
}
156+
157+
// Setup legacy controller
154158
binding.controls.player = player
155159
binding.controls.showTimeoutMs = CONTROLLER_SHOW_TIMEOUT
156160
binding.controls.setShowNextButton(false)

stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/feature/gallery/internal/AttachmentGalleryVideoPageFragment.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,9 @@ internal class AttachmentGalleryVideoPageFragment : Fragment() {
195195
private fun setupPlayerView(player: Player) {
196196
binding.playerView.apply {
197197
this.player = player
198-
controllerShowTimeoutMs = CONTROLLER_SHOW_TIMEOUT
199-
controllerAutoShow = false
200-
controllerHideOnTouch = true
198+
setControllerShowTimeoutMs(CONTROLLER_SHOW_TIMEOUT)
199+
setControllerAutoShow(false)
200+
setControllerHideOnTouch(true)
201201
setShowPreviousButton(false)
202202
setShowNextButton(false)
203203
setShowBuffering(PlayerView.SHOW_BUFFERING_NEVER)
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/*
2+
* Copyright (c) 2014-2025 Stream.io Inc. All rights reserved.
3+
*
4+
* Licensed under the Stream License;
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.getstream.chat.android.ui.widgets.internal
18+
19+
import android.content.Context
20+
import android.util.AttributeSet
21+
import android.view.LayoutInflater
22+
import android.widget.FrameLayout
23+
import androidx.annotation.OptIn
24+
import androidx.media3.common.Player
25+
import androidx.media3.common.util.UnstableApi
26+
import androidx.media3.ui.PlayerView
27+
import io.getstream.chat.android.core.internal.StreamHandsOff
28+
import io.getstream.chat.android.ui.R
29+
30+
/**
31+
* A custom view that wraps Media3's [androidx.media3.ui.PlayerView] with automatic inflation handling.
32+
*
33+
* This view automatically inflates the PlayerView layout with custom root and controller layouts
34+
* to avoid conflicts with different versions of the ExoPlayer library in integration projects.
35+
*
36+
* Usage:
37+
* ```xml
38+
* <io.getstream.chat.android.ui.widgets.internal.StreamPlayerView
39+
* android:id="@+id/streamPlayerView"
40+
* android:layout_width="match_parent"
41+
* android:layout_height="match_parent" />
42+
* ```
43+
*
44+
* Then in code:
45+
* ```kotlin
46+
* streamPlayerView.player = exoPlayer
47+
* streamPlayerView.setShowBuffering(PlayerView.SHOW_BUFFERING_NEVER)
48+
* ```
49+
*/
50+
51+
@OptIn(UnstableApi::class)
52+
internal class StreamPlayerView @JvmOverloads constructor(
53+
context: Context,
54+
attrs: AttributeSet? = null,
55+
defStyleAttr: Int = 0,
56+
) : FrameLayout(context, attrs, defStyleAttr) {
57+
58+
private val playerView: PlayerView
59+
60+
/**
61+
* Gets or sets the [androidx.media3.common.Player] instance.
62+
*/
63+
var player: Player?
64+
get() = playerView.player
65+
set(value) {
66+
playerView.player = value
67+
}
68+
69+
init {
70+
playerView = inflatePlayerView()
71+
addView(playerView)
72+
}
73+
74+
@StreamHandsOff(
75+
"This method manually inflates the PlayerView layout, because we are overriding the default " +
76+
"root layout (player_layout_id) and the default controller layout (controller_layout_id). " +
77+
"These layouts are just copies of the 'exo_player_view.xml' and the 'exo_player_control_view.xml' from " +
78+
"the ExoPlayer library. They only have a different name (layout ID) to avoid being overridden by layouts " +
79+
"with the same name from different versions of the ExoPlayer library (included in integration projects). " +
80+
"This ensures that we always use the correct layout for our version of the ExoPlayer library",
81+
)
82+
private fun inflatePlayerView(): PlayerView {
83+
return LayoutInflater
84+
.from(context)
85+
.inflate(R.layout.stream_ui_player_view, this, false) as PlayerView
86+
}
87+
88+
/**
89+
* Sets whether buffering should be shown.
90+
*
91+
* @param showBuffering One of [PlayerView.SHOW_BUFFERING_NEVER],
92+
* [PlayerView.SHOW_BUFFERING_WHEN_PLAYING], or [PlayerView.SHOW_BUFFERING_ALWAYS].
93+
*/
94+
fun setShowBuffering(showBuffering: Int) {
95+
playerView.setShowBuffering(showBuffering)
96+
}
97+
98+
/**
99+
* Sets the artwork display mode.
100+
*
101+
* @param artworkDisplayMode One of [PlayerView.ARTWORK_DISPLAY_MODE_OFF],
102+
* [PlayerView.ARTWORK_DISPLAY_MODE_FIT], or [PlayerView.ARTWORK_DISPLAY_MODE_FILL].
103+
*/
104+
fun setArtworkDisplayMode(artworkDisplayMode: Int) {
105+
playerView.artworkDisplayMode = artworkDisplayMode
106+
}
107+
108+
/**
109+
* Sets whether the controller should be shown.
110+
*
111+
* @param useController Whether to use the controller.
112+
*/
113+
fun setUseController(useController: Boolean) {
114+
playerView.useController = useController
115+
}
116+
117+
/**
118+
* Sets whether the controller should be shown automatically.
119+
*
120+
* @param autoShow Whether to show the controller automatically.
121+
*/
122+
fun setControllerAutoShow(autoShow: Boolean) {
123+
playerView.controllerAutoShow = autoShow
124+
}
125+
126+
/**
127+
* Sets the controller show timeout.
128+
*
129+
* @param timeout the timeout in milliseconds.
130+
*/
131+
fun setControllerShowTimeoutMs(timeout: Int) {
132+
playerView.controllerShowTimeoutMs = timeout
133+
}
134+
135+
/**
136+
* Sets whether the controller should hide on touch.
137+
*
138+
* @param controllerHideOnTouch Whether the controller should hide on touch.
139+
*/
140+
fun setControllerHideOnTouch(controllerHideOnTouch: Boolean) {
141+
playerView.controllerHideOnTouch = controllerHideOnTouch
142+
}
143+
144+
/**
145+
* Sets whether the "Previous" button shown be shown.
146+
*
147+
* @param show Whether the "Previous" button should be shown.
148+
*/
149+
fun setShowPreviousButton(show: Boolean) {
150+
playerView.setShowPreviousButton(show)
151+
}
152+
153+
/**
154+
* Sets whether the "Next" button shown be shown.
155+
*
156+
* @param show Whether the "Next" button should be shown.
157+
*/
158+
fun setShowNextButton(show: Boolean) {
159+
playerView.setShowNextButton(show)
160+
}
161+
}

stream-chat-android-ui-components/src/main/res/layout/stream_ui_activity_attachment_media.xml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,21 +76,23 @@
7676
>
7777

7878
<LinearLayout
79-
android:orientation="vertical"
8079
android:layout_width="match_parent"
81-
android:layout_height="match_parent" >
82-
<androidx.media3.ui.PlayerView
80+
android:layout_height="match_parent"
81+
android:orientation="vertical"
82+
>
83+
84+
<io.getstream.chat.android.ui.widgets.internal.StreamPlayerView
8385
android:id="@+id/playerView"
8486
android:layout_width="match_parent"
8587
android:layout_height="0dp"
8688
android:layout_weight="1"
87-
app:use_controller="false"
8889
/>
8990

9091
<androidx.media3.ui.LegacyPlayerControlView
9192
android:id="@+id/controls"
9293
android:layout_width="match_parent"
93-
android:layout_height="wrap_content" />
94+
android:layout_height="wrap_content"
95+
/>
9496
</LinearLayout>
9597

9698
<ImageView

0 commit comments

Comments
 (0)