From 49f3080b9e64840d6a0f96ca88e7f8d18021c7bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joris=20Pelgr=C3=B6m?= Date: Sat, 5 Aug 2023 02:07:52 +0200 Subject: [PATCH] Update ExoPlayer usage (#3760) * Switch to StyledPlayerView for migration * Migrate to media3 for ExoPlayer * Replace deprecated code * Restore/customize layout to ExoPlayer v2 --- app/build.gradle.kts | 8 +- .../android/webview/WebViewActivity.kt | 89 +++----- .../drawable/ic_baseline_fullscreen_24.xml | 10 - .../ic_baseline_fullscreen_exit_24.xml | 10 - .../drawable/ic_baseline_volume_off_24.xml | 3 +- .../res/drawable/ic_baseline_volume_up_24.xml | 3 +- app/src/main/res/layout/activity_webview.xml | 7 +- .../res/layout/exo_player_control_view.xml | 193 ++++++++++++------ app/src/main/res/layout/exo_player_view.xml | 23 --- app/src/main/res/values/styles.xml | 5 + automotive/build.gradle.kts | 8 +- common/src/main/res/values/strings.xml | 1 + gradle/libs.versions.toml | 13 +- 13 files changed, 188 insertions(+), 185 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_baseline_fullscreen_24.xml delete mode 100644 app/src/main/res/drawable/ic_baseline_fullscreen_exit_24.xml delete mode 100644 app/src/main/res/layout/exo_player_view.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 104ce1c48..bbf932fce 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -159,11 +159,9 @@ dependencies { implementation(libs.biometric) implementation(libs.webkit) - implementation(libs.exoplayer.core) - implementation(libs.exoplayer.hls) - implementation(libs.exoplayer.ui) - "fullImplementation"(libs.extension.cronet) - "minimalImplementation"(libs.extension.cronet) { + implementation(libs.bundles.media3) + "fullImplementation"(libs.media3.datasource.cronet) + "minimalImplementation"(libs.media3.datasource.cronet) { exclude(group = "com.google.android.gms", module = "play-services-cronet") } "minimalImplementation"(libs.cronet.embedded) diff --git a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt index 423e6e9f2..f03f9959c 100644 --- a/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt +++ b/app/src/main/java/io/homeassistant/companion/android/webview/WebViewActivity.kt @@ -42,15 +42,15 @@ import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView import android.widget.FrameLayout -import android.widget.ImageView +import android.widget.ImageButton import android.widget.Toast import androidx.activity.OnBackPressedCallback import androidx.activity.result.IntentSenderRequest import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.OptIn import androidx.annotation.RequiresApi import androidx.appcompat.app.AlertDialog import androidx.core.app.ActivityCompat -import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.ColorUtils @@ -59,17 +59,17 @@ import androidx.core.view.WindowInsetsControllerCompat import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import androidx.media3.common.MediaItem +import androidx.media3.common.Player +import androidx.media3.common.VideoSize +import androidx.media3.datasource.cronet.CronetDataSource +import androidx.media3.exoplayer.DefaultLoadControl +import androidx.media3.exoplayer.ExoPlayer +import androidx.media3.exoplayer.source.DefaultMediaSourceFactory +import androidx.media3.ui.AspectRatioFrameLayout +import androidx.media3.ui.PlayerView import androidx.webkit.WebViewCompat import androidx.webkit.WebViewFeature -import com.google.android.exoplayer2.DefaultLoadControl -import com.google.android.exoplayer2.MediaItem -import com.google.android.exoplayer2.Player -import com.google.android.exoplayer2.SimpleExoPlayer -import com.google.android.exoplayer2.ext.cronet.CronetDataSource -import com.google.android.exoplayer2.source.DefaultMediaSourceFactory -import com.google.android.exoplayer2.ui.AspectRatioFrameLayout -import com.google.android.exoplayer2.ui.PlayerView -import com.google.android.exoplayer2.video.VideoSize import dagger.hilt.android.AndroidEntryPoint import eightbitlab.com.blurview.RenderScriptBlur import io.homeassistant.companion.android.BaseActivity @@ -85,7 +85,6 @@ import io.homeassistant.companion.android.database.authentication.Authentication import io.homeassistant.companion.android.database.authentication.AuthenticationDao import io.homeassistant.companion.android.databinding.ActivityWebviewBinding import io.homeassistant.companion.android.databinding.DialogAuthenticationBinding -import io.homeassistant.companion.android.databinding.ExoPlayerViewBinding import io.homeassistant.companion.android.launch.LaunchActivity import io.homeassistant.companion.android.matter.MatterFrontendCommissioningStatus import io.homeassistant.companion.android.nfc.WriteNfcTag @@ -118,6 +117,7 @@ import java.util.concurrent.Executors import javax.inject.Inject import io.homeassistant.companion.android.common.R as commonR +@OptIn(androidx.media3.common.util.UnstableApi::class) @AndroidEntryPoint class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webview.WebView { @@ -196,7 +196,6 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi private lateinit var myCustomView: View private lateinit var authenticator: Authenticator private lateinit var exoPlayerView: PlayerView - private lateinit var playerBinding: ExoPlayerViewBinding private lateinit var windowInsetsController: WindowInsetsControllerCompat private var mFilePathCallback: ValueCallback>? = null @@ -208,13 +207,13 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi private var firstAuthTime: Long = 0 private var resourceURL: String = "" private var appLocked = true - private var exoPlayer: SimpleExoPlayer? = null + private var exoPlayer: ExoPlayer? = null private var isExoFullScreen = false - private var exoTop: Int = 0 // These margins are from the DOM and scaled to screen - private var exoLeft: Int = 0 - private var exoRight: Int = 0 - private var exoBottom: Int = 0 - private var exoMute: Boolean = true + private var exoTop = 0 // These margins are from the DOM and scaled to screen + private var exoLeft = 0 + private var exoRight = 0 + private var exoBottom = 0 + private var exoMute = true private var failedConnection = "external" private var clearHistory = false private var moreInfoEntity = "" @@ -253,12 +252,8 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi exoPlayerView.visibility = View.GONE exoPlayerView.setBackgroundColor(Color.BLACK) exoPlayerView.alpha = 1f - exoPlayerView.setShowBuffering(PlayerView.SHOW_BUFFERING_ALWAYS) - exoPlayerView.controllerHideOnTouch = true exoPlayerView.controllerShowTimeoutMs = 2000 - playerBinding = ExoPlayerViewBinding.bind(exoPlayerView) - appLocked = presenter.isAppLocked() binding.blurView.setBlurEnabled(appLocked) @@ -852,7 +847,7 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi val uri = Uri.parse(payload.getString("url")) exoMute = payload.optBoolean("muted") runOnUiThread { - exoPlayer = SimpleExoPlayer.Builder(applicationContext).setMediaSourceFactory( + exoPlayer = ExoPlayer.Builder(applicationContext).setMediaSourceFactory( DefaultMediaSourceFactory( CronetDataSource.Factory( CronetEngine.Builder(applicationContext).enableQuic(true).build(), @@ -872,6 +867,7 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi exoPlayer?.addListener(object : Player.Listener { override fun onVideoSizeChanged(videoSize: VideoSize) { super.onVideoSizeChanged(videoSize) + if (videoSize.height == 0 || videoSize.width == 0) return exoBottom = exoTop + ((exoRight - exoLeft) * videoSize.height / videoSize.width) runOnUiThread { @@ -880,16 +876,15 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi } }) exoPlayer?.prepare() - exoMute = !exoMute + exoMute = !exoMute // Invert because exoToggleMute() will invert again exoToggleMute() - exoPlayerView.player = exoPlayer - exoPlayerView.visibility = View.VISIBLE - - findViewById(R.id.exo_fullscreen_icon).setOnClickListener { - isExoFullScreen = !isExoFullScreen + exoPlayerView.setFullscreenButtonClickListener { isFullScreen -> + isExoFullScreen = isFullScreen exoResizeLayout() } - findViewById(R.id.exo_mute_icon).setOnClickListener { exoToggleMute() } + exoPlayerView.player = exoPlayer + exoPlayerView.visibility = View.VISIBLE + findViewById(R.id.exo_ha_mute)?.setOnClickListener { exoToggleMute() } } webView.externalBus( id = json.get("id"), @@ -932,20 +927,10 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi exoMute = !exoMute if (exoMute) { exoPlayer?.volume = 0f - findViewById(R.id.exo_mute_icon).setImageDrawable( - ContextCompat.getDrawable( - applicationContext, - R.drawable.ic_baseline_volume_off_24 - ) - ) + findViewById(R.id.exo_ha_mute)?.setImageResource(R.drawable.ic_baseline_volume_off_24) } else { exoPlayer?.volume = 1f - findViewById(R.id.exo_mute_icon).setImageDrawable( - ContextCompat.getDrawable( - applicationContext, - R.drawable.ic_baseline_volume_up_24 - ) - ) + findViewById(R.id.exo_ha_mute)?.setImageResource(R.drawable.ic_baseline_volume_up_24) } } @@ -953,22 +938,16 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi val exoLayoutParams = exoPlayerView.layoutParams as FrameLayout.LayoutParams if (isExoFullScreen) { if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { - playerBinding.exoContentFrame.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL + exoPlayerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL } else { - playerBinding.exoContentFrame.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIXED_WIDTH + exoPlayerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIXED_WIDTH } exoLayoutParams.setMargins(0, 0, 0, 0) exoPlayerView.layoutParams.height = FrameLayout.LayoutParams.MATCH_PARENT exoPlayerView.layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT - findViewById(R.id.exo_fullscreen_icon).setImageDrawable( - ContextCompat.getDrawable( - applicationContext, - R.drawable.ic_baseline_fullscreen_exit_24 - ) - ) hideSystemUI() } else { - playerBinding.exoContentFrame.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL + exoPlayerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FILL exoPlayerView.layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT exoPlayerView.layoutParams.width = FrameLayout.LayoutParams.MATCH_PARENT val screenWidth: Int = resources.displayMetrics.widthPixels @@ -979,12 +958,6 @@ class WebViewActivity : BaseActivity(), io.homeassistant.companion.android.webvi maxOf(screenWidth - exoRight, 0), maxOf(screenHeight - exoBottom, 0) ) - findViewById(R.id.exo_fullscreen_icon).setImageDrawable( - ContextCompat.getDrawable( - applicationContext, - R.drawable.ic_baseline_fullscreen_24 - ) - ) showSystemUI() } exoPlayerView.requestLayout() diff --git a/app/src/main/res/drawable/ic_baseline_fullscreen_24.xml b/app/src/main/res/drawable/ic_baseline_fullscreen_24.xml deleted file mode 100644 index 804cebcfe..000000000 --- a/app/src/main/res/drawable/ic_baseline_fullscreen_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_baseline_fullscreen_exit_24.xml b/app/src/main/res/drawable/ic_baseline_fullscreen_exit_24.xml deleted file mode 100644 index 1efbe0a9d..000000000 --- a/app/src/main/res/drawable/ic_baseline_fullscreen_exit_24.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_baseline_volume_off_24.xml b/app/src/main/res/drawable/ic_baseline_volume_off_24.xml index aaf49e9be..9a8a9e8a6 100644 --- a/app/src/main/res/drawable/ic_baseline_volume_off_24.xml +++ b/app/src/main/res/drawable/ic_baseline_volume_off_24.xml @@ -2,8 +2,7 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/drawable/ic_baseline_volume_up_24.xml b/app/src/main/res/drawable/ic_baseline_volume_up_24.xml index 0db34695f..0dc6b0ca5 100644 --- a/app/src/main/res/drawable/ic_baseline_volume_up_24.xml +++ b/app/src/main/res/drawable/ic_baseline_volume_up_24.xml @@ -2,8 +2,7 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24" - android:tint="?attr/colorControlNormal"> + android:viewportHeight="24"> diff --git a/app/src/main/res/layout/activity_webview.xml b/app/src/main/res/layout/activity_webview.xml index 95e64eb38..6f4ed9982 100644 --- a/app/src/main/res/layout/activity_webview.xml +++ b/app/src/main/res/layout/activity_webview.xml @@ -20,11 +20,12 @@ android:id="@+id/exoviewGroup" android:layout_width="match_parent" android:layout_height="match_parent"> - - + android:layout_height="wrap_content" + app:show_buffering="always"> + \ No newline at end of file diff --git a/app/src/main/res/layout/exo_player_control_view.xml b/app/src/main/res/layout/exo_player_control_view.xml index 0698cd7af..28ac7bf8f 100644 --- a/app/src/main/res/layout/exo_player_control_view.xml +++ b/app/src/main/res/layout/exo_player_control_view.xml @@ -1,67 +1,136 @@ - - + + + + + + - - + android:layout_height="@dimen/exo_styled_bottom_bar_height" + android:layout_marginTop="@dimen/exo_styled_bottom_bar_margin_top" + android:layout_gravity="bottom" + android:background="@color/exo_bottom_bar_background" + android:layoutDirection="ltr"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + android:id="@id/exo_center_controls" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:background="@android:color/transparent" + android:gravity="center" + android:padding="@dimen/exo_styled_controls_padding" + android:clipToPadding="false" + android:layoutDirection="ltr"> + + + + + + + - + + \ No newline at end of file diff --git a/app/src/main/res/layout/exo_player_view.xml b/app/src/main/res/layout/exo_player_view.xml deleted file mode 100644 index 748de5440..000000000 --- a/app/src/main/res/layout/exo_player_view.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index cc820863d..7069421ab 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -131,4 +131,9 @@ true false + + diff --git a/automotive/build.gradle.kts b/automotive/build.gradle.kts index d456a4bee..1b5946360 100644 --- a/automotive/build.gradle.kts +++ b/automotive/build.gradle.kts @@ -188,11 +188,9 @@ dependencies { implementation(libs.biometric) implementation(libs.webkit) - implementation(libs.exoplayer.core) - implementation(libs.exoplayer.hls) - implementation(libs.exoplayer.ui) - "fullImplementation"(libs.extension.cronet) - "minimalImplementation"(libs.extension.cronet) { + implementation(libs.bundles.media3) + "fullImplementation"(libs.media3.datasource.cronet) + "minimalImplementation"(libs.media3.datasource.cronet) { exclude(group = "com.google.android.gms", module = "play-services-cronet") } "minimalImplementation"(libs.cronet.embedded) diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index 9feb477de..a638f54c3 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -397,6 +397,7 @@ No connected Wear devices, please make sure Bluetooth is on and your watch is paired. The Wear app is installed on some of your Wear devices: (%1$s)\n\nClick the button below to install the app on the other devices. Please open the Home Assistant app and send the command again in order to grant the proper permissions. You will be taken to a page to either grant the Home Assistant app the permission, or you will need to select Permissions from the details page and then grant the missing permission. For command_bluetooth the name of the permission is Nearby devices. If you are attempting to use command_activity to make a phone call you will also need to grant Phone permissions. + Mute/Unmute Areas More entities Need Help? diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e59e9d8aa..eb4e16e99 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,7 +23,6 @@ converterJackson = "2.9.0" coreKtx = "1.10.1" cronet-embedded = "113.5672.61" emojiJava = "5.1.1" -exoplayer = "2.19.0" firebase-bom = "32.2.0" firebaseAppdistributionGradle = "4.0.0" fragment-ktx = "1.6.1" @@ -40,6 +39,7 @@ ktlint = "11.5.0" lifecycle = "2.6.1" loggingInterceptor = "4.11.0" material = "1.9.0" +media3 = "1.1.0" navigation-compose = "2.6.0" okhttp = "4.11.0" picasso = "2.8" @@ -110,10 +110,6 @@ converter-jackson = { module = "com.squareup.retrofit2:converter-jackson", versi core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" } cronet-embedded = { module = "org.chromium.net:cronet-embedded", version.ref = "cronet-embedded" } emojiJava = { module = "com.vdurmont:emoji-java", version.ref = "emojiJava" } -extension-cronet = { module = "com.google.android.exoplayer:extension-cronet", version.ref = "exoplayer" } -exoplayer-ui = { module = "com.google.android.exoplayer:exoplayer-ui", version.ref = "exoplayer" } -exoplayer-hls = { module = "com.google.android.exoplayer:exoplayer-hls", version.ref = "exoplayer" } -exoplayer-core = { module = "com.google.android.exoplayer:exoplayer-core", version.ref = "exoplayer" } firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebase-bom" } firebase-messaging = { module = "com.google.firebase:firebase-messaging" } fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragment-ktx" } @@ -134,6 +130,10 @@ logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", ver navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigation-compose" } okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } material = { module = "com.google.android.material:material", version.ref = "material" } +media3-datasource-cronet = { module = "androidx.media3:media3-datasource-cronet", version.ref = "media3" } +media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" } +media3-exoplayer-hls = { module = "androidx.media3:media3-exoplayer-hls", version.ref = "media3" } +media3-ui = { module = "androidx.media3:media3-ui", version.ref = "media3" } play-services-threadnetwork = { module = "com.google.android.gms:play-services-threadnetwork", version.ref = "play-services-threadnetwork" } play-services-home = { module = "com.google.android.gms:play-services-home", version.ref = "play-services-home" } play-services-location = { module = "com.google.android.gms:play-services-location", version.ref = "play-services-location" } @@ -154,3 +154,6 @@ wear-remote-interactions = { module = "androidx.wear:wear-remote-interactions", wear-tiles-material = { module = "androidx.wear.tiles:tiles-material", version.ref = "wear-tiles" } wear-tiles = { module = "androidx.wear.tiles:tiles", version.ref = "wear-tiles" } webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" } + +[bundles] +media3 = ["media3-exoplayer", "media3-exoplayer-hls", "media3-ui"] \ No newline at end of file