Browse Source

Merge pull request #13092 from nextcloud/bugfix/audio-file-preview

Bugfix Audio File Preview
Alper Öztürk 1 year ago
parent
commit
49126124a5

+ 46 - 19
app/src/main/java/com/nextcloud/client/media/PlayerService.kt

@@ -16,6 +16,7 @@ import android.os.IBinder
 import android.widget.MediaController
 import android.widget.Toast
 import androidx.core.app.NotificationCompat
+import androidx.localbroadcastmanager.content.LocalBroadcastManager
 import com.nextcloud.client.account.User
 import com.nextcloud.client.network.ClientFactory
 import com.nextcloud.utils.ForegroundServiceHelper
@@ -23,7 +24,9 @@ import com.nextcloud.utils.extensions.getParcelableArgument
 import com.owncloud.android.R
 import com.owncloud.android.datamodel.ForegroundServiceType
 import com.owncloud.android.datamodel.OCFile
+import com.owncloud.android.lib.common.utils.Log_OC
 import com.owncloud.android.ui.notifications.NotificationUtils
+import com.owncloud.android.ui.preview.PreviewMediaActivity
 import com.owncloud.android.utils.theme.ViewThemeUtils
 import dagger.android.AndroidInjection
 import java.util.Locale
@@ -32,6 +35,8 @@ import javax.inject.Inject
 class PlayerService : Service() {
 
     companion object {
+        private const val TAG = "PlayerService"
+
         const val EXTRA_USER = "USER"
         const val EXTRA_FILE = "FILE"
         const val EXTRA_AUTO_PLAY = "EXTRA_AUTO_PLAY"
@@ -40,6 +45,8 @@ class PlayerService : Service() {
         const val ACTION_STOP = "STOP"
         const val ACTION_TOGGLE = "TOGGLE"
         const val ACTION_STOP_FILE = "STOP_FILE"
+
+        const val IS_MEDIA_CONTROL_LAYOUT_READY = "IS_MEDIA_CONTROL_LAYOUT_READY"
     }
 
     class Binder(val service: PlayerService) : android.os.Binder() {
@@ -52,24 +59,34 @@ class PlayerService : Service() {
     }
 
     private val playerListener = object : Player.Listener {
-
         override fun onRunning(file: OCFile) {
+            Log_OC.d(TAG, "PlayerService.onRunning()")
+            val intent = Intent(PreviewMediaActivity.MEDIA_CONTROL_READY_RECEIVER).apply {
+                putExtra(IS_MEDIA_CONTROL_LAYOUT_READY, false)
+            }
+            LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)
             startForeground(file)
         }
 
         override fun onStart() {
-            // empty
+            Log_OC.d(TAG, "PlayerService.onStart()")
+            val intent = Intent(PreviewMediaActivity.MEDIA_CONTROL_READY_RECEIVER).apply {
+                putExtra(IS_MEDIA_CONTROL_LAYOUT_READY, true)
+            }
+            LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent)
         }
 
         override fun onPause() {
-            // empty
+            Log_OC.d(TAG, "PlayerService.onPause()")
         }
 
         override fun onStop() {
+            Log_OC.d(TAG, "PlayerService.onStop()")
             stopServiceAndRemoveNotification(null)
         }
 
         override fun onError(error: PlayerError) {
+            Log_OC.d(TAG, "PlayerService.onError()")
             Toast.makeText(this@PlayerService, error.message, Toast.LENGTH_SHORT).show()
         }
     }
@@ -89,18 +106,23 @@ class PlayerService : Service() {
 
     override fun onCreate() {
         super.onCreate()
+
         AndroidInjection.inject(this)
         player = Player(applicationContext, clientFactory, playerListener, audioManager)
         notificationBuilder = NotificationCompat.Builder(this)
         viewThemeUtils.androidx.themeNotificationCompatBuilder(this, notificationBuilder)
 
-        val stop = Intent(this, PlayerService::class.java)
-        stop.action = ACTION_STOP
+        val stop = Intent(this, PlayerService::class.java).apply {
+            action = ACTION_STOP
+        }
+
         val pendingStop = PendingIntent.getService(this, 0, stop, PendingIntent.FLAG_IMMUTABLE)
         notificationBuilder.addAction(0, getString(R.string.player_stop).toUpperCase(Locale.getDefault()), pendingStop)
 
-        val toggle = Intent(this, PlayerService::class.java)
-        toggle.action = ACTION_TOGGLE
+        val toggle = Intent(this, PlayerService::class.java).apply {
+            action = ACTION_TOGGLE
+        }
+
         val pendingToggle = PendingIntent.getService(this, 0, toggle, PendingIntent.FLAG_IMMUTABLE)
         notificationBuilder.addAction(
             0,
@@ -124,10 +146,12 @@ class PlayerService : Service() {
     }
 
     private fun onActionToggle() {
-        if (player.isPlaying) {
-            player.pause()
-        } else {
-            player.start()
+        player.run {
+            if (isPlaying) {
+                pause()
+            } else {
+                start()
+            }
         }
     }
 
@@ -153,14 +177,17 @@ class PlayerService : Service() {
     private fun startForeground(currentFile: OCFile) {
         val ticker = String.format(getString(R.string.media_notif_ticker), getString(R.string.app_name))
         val content = getString(R.string.media_state_playing, currentFile.getFileName())
-        notificationBuilder.setSmallIcon(R.drawable.ic_play_arrow)
-        notificationBuilder.setWhen(System.currentTimeMillis())
-        notificationBuilder.setOngoing(true)
-        notificationBuilder.setContentTitle(ticker)
-        notificationBuilder.setContentText(content)
-
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            notificationBuilder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_MEDIA)
+
+        notificationBuilder.run {
+            setSmallIcon(R.drawable.ic_play_arrow)
+            setWhen(System.currentTimeMillis())
+            setOngoing(true)
+            setContentTitle(ticker)
+            setContentText(content)
+
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+                setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_MEDIA)
+            }
         }
 
         ForegroundServiceHelper.startService(

+ 8 - 6
app/src/main/java/com/nextcloud/client/media/PlayerServiceConnection.kt

@@ -38,12 +38,14 @@ class PlayerServiceConnection(private val context: Context) : MediaController.Me
     }
 
     fun start(user: User, file: OCFile, playImmediately: Boolean, position: Long) {
-        val i = Intent(context, PlayerService::class.java)
-        i.putExtra(PlayerService.EXTRA_USER, user)
-        i.putExtra(PlayerService.EXTRA_FILE, file)
-        i.putExtra(PlayerService.EXTRA_AUTO_PLAY, playImmediately)
-        i.putExtra(PlayerService.EXTRA_START_POSITION_MS, position)
-        i.action = PlayerService.ACTION_PLAY
+        val i = Intent(context, PlayerService::class.java).apply {
+            putExtra(PlayerService.EXTRA_USER, user)
+            putExtra(PlayerService.EXTRA_FILE, file)
+            putExtra(PlayerService.EXTRA_AUTO_PLAY, playImmediately)
+            putExtra(PlayerService.EXTRA_START_POSITION_MS, position)
+            action = PlayerService.ACTION_PLAY
+        }
+
         startForegroundService(i)
     }
 

+ 68 - 18
app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaActivity.kt

@@ -13,7 +13,10 @@
 package com.owncloud.android.ui.preview
 
 import android.app.Activity
+import android.content.BroadcastReceiver
+import android.content.Context
 import android.content.Intent
+import android.content.IntentFilter
 import android.content.res.Configuration
 import android.graphics.Bitmap
 import android.graphics.BitmapFactory
@@ -41,6 +44,7 @@ import androidx.core.view.WindowInsetsControllerCompat
 import androidx.core.view.marginBottom
 import androidx.core.view.updateLayoutParams
 import androidx.core.view.updatePadding
+import androidx.localbroadcastmanager.content.LocalBroadcastManager
 import androidx.media3.common.MediaItem
 import androidx.media3.common.util.UnstableApi
 import androidx.media3.exoplayer.ExoPlayer
@@ -53,6 +57,7 @@ import com.nextcloud.client.jobs.BackgroundJobManager
 import com.nextcloud.client.jobs.download.FileDownloadHelper
 import com.nextcloud.client.media.ExoplayerListener
 import com.nextcloud.client.media.NextcloudExoPlayer.createNextcloudExoplayer
+import com.nextcloud.client.media.PlayerService
 import com.nextcloud.client.media.PlayerServiceConnection
 import com.nextcloud.client.network.ClientFactory
 import com.nextcloud.client.network.ClientFactory.CreationException
@@ -146,7 +151,26 @@ class PreviewMediaActivity :
         showMediaTypeViews()
         configureSystemBars()
         emptyListView = binding.emptyView.emptyListView
-        setLoadingView()
+        showProgressLayout()
+    }
+
+    private fun registerMediaControlReceiver() {
+        val filter = IntentFilter(MEDIA_CONTROL_READY_RECEIVER)
+        LocalBroadcastManager.getInstance(this).registerReceiver(mediaControlReceiver, filter)
+    }
+
+    private val mediaControlReceiver: BroadcastReceiver = object : BroadcastReceiver() {
+        override fun onReceive(context: Context, intent: Intent) {
+            intent.getBooleanExtra(PlayerService.IS_MEDIA_CONTROL_LAYOUT_READY, false).run {
+                if (this) {
+                    hideProgressLayout()
+                    mediaPlayerServiceConnection?.bind()
+                    setupAudioPlayerServiceConnection()
+                } else {
+                    showProgressLayout()
+                }
+            }
+        }
     }
 
     private fun initArguments(savedInstanceState: Bundle?) {
@@ -160,6 +184,20 @@ class PreviewMediaActivity :
         } else {
             initWithBundle(savedInstanceState)
         }
+
+        if (MimeTypeUtil.isAudio(file)) {
+            preparePreviewForAudioFile()
+        }
+    }
+
+    private fun preparePreviewForAudioFile() {
+        registerMediaControlReceiver()
+
+        if (file.isDown) {
+            return
+        }
+
+        requestForDownload(file)
     }
 
     private fun initWithIntent(intent: Intent) {
@@ -206,11 +244,18 @@ class PreviewMediaActivity :
         )
     }
 
-    private fun setLoadingView() {
+    private fun showProgressLayout() {
         binding.progress.visibility = View.VISIBLE
+        binding.mediaController.visibility = View.GONE
         binding.emptyView.emptyListView.visibility = View.GONE
     }
 
+    private fun hideProgressLayout() {
+        binding.progress.visibility = View.GONE
+        binding.mediaController.visibility = View.VISIBLE
+        binding.emptyView.emptyListView.visibility = View.VISIBLE
+    }
+
     private fun setVideoErrorMessage(headline: String, @StringRes message: Int) {
         binding.emptyView.run {
             emptyListViewHeadline.text = headline
@@ -218,8 +263,8 @@ class PreviewMediaActivity :
             emptyListIcon.setImageResource(R.drawable.file_movie)
             emptyListViewText.visibility = View.VISIBLE
             emptyListIcon.visibility = View.VISIBLE
-            binding.progress.visibility = View.GONE
-            emptyListView.visibility = View.VISIBLE
+
+            hideProgressLayout()
         }
     }
 
@@ -311,21 +356,23 @@ class PreviewMediaActivity :
 
         Log_OC.v(TAG, "onStart")
 
-        if (file != null) {
-            mediaPlayerServiceConnection?.bind()
+        if (file == null) {
+            return
+        }
 
-            if (MimeTypeUtil.isAudio(file)) {
-                setupAudioPlayerServiceConnection()
-            } else if (MimeTypeUtil.isVideo(file)) {
-                if (mediaPlayerServiceConnection?.isConnected == true) {
-                    stopAudio()
-                }
+        mediaPlayerServiceConnection?.bind()
 
-                if (exoPlayer != null) {
-                    playVideo()
-                } else {
-                    initNextcloudExoPlayer()
-                }
+        if (MimeTypeUtil.isAudio(file)) {
+            setupAudioPlayerServiceConnection()
+        } else if (MimeTypeUtil.isVideo(file)) {
+            if (mediaPlayerServiceConnection?.isConnected == true) {
+                stopAudio()
+            }
+
+            if (exoPlayer != null) {
+                playVideo()
+            } else {
+                initNextcloudExoPlayer()
             }
         }
     }
@@ -689,8 +736,9 @@ class PreviewMediaActivity :
     override fun onDestroy() {
         Log_OC.v(TAG, "onDestroy")
 
-        super.onDestroy()
+        LocalBroadcastManager.getInstance(this).unregisterReceiver(mediaControlReceiver)
 
+        super.onDestroy()
         exoPlayer?.run {
             stop()
             release()
@@ -781,6 +829,8 @@ class PreviewMediaActivity :
 
     companion object {
         private val TAG = PreviewMediaActivity::class.java.simpleName
+
+        const val MEDIA_CONTROL_READY_RECEIVER: String = "MEDIA_CONTROL_READY_RECEIVER"
         const val EXTRA_FILE = "FILE"
         const val EXTRA_USER = "USER"
         const val EXTRA_AUTOPLAY = "AUTOPLAY"

+ 7 - 5
app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java

@@ -177,12 +177,14 @@ public class PreviewMediaFragment extends FileFragment implements OnTouchListene
 
         Bundle bundle = getArguments();
 
-        setFile(BundleExtensionsKt.getParcelableArgument(bundle, FILE, OCFile.class));
-        user = BundleExtensionsKt.getParcelableArgument(bundle, USER, User.class);
+        if (bundle != null) {
+            setFile(BundleExtensionsKt.getParcelableArgument(bundle, FILE, OCFile.class));
+            user = BundleExtensionsKt.getParcelableArgument(bundle, USER, User.class);
 
-        savedPlaybackPosition = bundle.getLong(PLAYBACK_POSITION);
-        autoplay = bundle.getBoolean(AUTOPLAY);
-        isLivePhoto = bundle.getBoolean(IS_LIVE_PHOTO);
+            savedPlaybackPosition = bundle.getLong(PLAYBACK_POSITION);
+            autoplay = bundle.getBoolean(AUTOPLAY);
+            isLivePhoto = bundle.getBoolean(IS_LIVE_PHOTO);
+        }
 
         mediaPlayerServiceConnection = new PlayerServiceConnection(requireContext());
     }

+ 5 - 13
app/src/main/res/layout/activity_preview_media.xml

@@ -51,24 +51,16 @@
 
     <FrameLayout
         android:id="@+id/progress"
+        android:background="@color/color_dark_transparent"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-        <com.elyeproj.loaderviewlibrary.LoaderImageView
-            android:layout_width="@dimen/empty_list_icon_layout_width"
-            android:layout_height="@dimen/empty_list_icon_layout_width"
+        <com.google.android.material.progressindicator.CircularProgressIndicator
+            android:layout_width="wrap_content"
             android:layout_gravity="center"
-            android:contentDescription="@null"
-            app:corners="24" />
+            android:layout_height="wrap_content"
+            android:indeterminate="true" />
 
-        <ImageView
-            android:layout_width="@dimen/empty_list_icon_layout_width"
-            android:layout_height="@dimen/empty_list_icon_layout_height"
-            android:layout_gravity="center"
-            android:contentDescription="@null"
-            android:padding="@dimen/standard_half_padding"
-            android:src="@drawable/file_movie"
-            app:tint="@color/bg_default" />
     </FrameLayout>
 
     <FrameLayout