diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 81a29817..05675554 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,7 +5,6 @@ - diff --git a/app/src/main/java/com/futo/platformplayer/AdvancedOrientationListener.kt b/app/src/main/java/com/futo/platformplayer/AdvancedOrientationListener.kt index acc0baca..2b0897d5 100644 --- a/app/src/main/java/com/futo/platformplayer/AdvancedOrientationListener.kt +++ b/app/src/main/java/com/futo/platformplayer/AdvancedOrientationListener.kt @@ -77,10 +77,14 @@ class AdvancedOrientationListener(private val activity: Activity, private val li lastStableOrientation = newOrientation lifecycleScope.launch(Dispatchers.Main) { - delay(stabilityThresholdTime) - if (newOrientation == lastStableOrientation) { - lastOrientation = newOrientation - onOrientationChanged.emit(newOrientation) + try { + delay(stabilityThresholdTime) + if (newOrientation == lastStableOrientation) { + lastOrientation = newOrientation + onOrientationChanged.emit(newOrientation) + } + } catch (e: Throwable) { + Logger.i(TAG, "Failed to trigger onOrientationChanged", e) } } } @@ -111,4 +115,8 @@ class AdvancedOrientationListener(private val activity: Activity, private val li fun stopListening() { sensorManager.unregisterListener(sensorListener) } + + companion object { + private val TAG = "AdvancedOrientationListener" + } } diff --git a/app/src/main/java/com/futo/platformplayer/Settings.kt b/app/src/main/java/com/futo/platformplayer/Settings.kt index eb83e298..4c205620 100644 --- a/app/src/main/java/com/futo/platformplayer/Settings.kt +++ b/app/src/main/java/com/futo/platformplayer/Settings.kt @@ -487,7 +487,7 @@ class Settings : FragmentedStorageFileJson() { class CommentSettings { @FormField(R.string.default_comment_section, FieldForm.DROPDOWN, -1, 0) @DropdownFieldOptionsId(R.array.comment_sections) - var defaultCommentSection: Int = 0; + var defaultCommentSection: Int = 1; @FormField(R.string.bad_reputation_comments_fading, FieldForm.TOGGLE, R.string.bad_reputation_comments_fading_description, 0) var badReputationCommentsFading: Boolean = true; diff --git a/app/src/main/java/com/futo/platformplayer/SimpleOrientationListener.kt b/app/src/main/java/com/futo/platformplayer/SimpleOrientationListener.kt index 24c86095..6d231f9d 100644 --- a/app/src/main/java/com/futo/platformplayer/SimpleOrientationListener.kt +++ b/app/src/main/java/com/futo/platformplayer/SimpleOrientationListener.kt @@ -5,6 +5,7 @@ import android.content.pm.ActivityInfo import android.hardware.SensorManager import android.view.OrientationEventListener import com.futo.platformplayer.constructs.Event1 +import com.futo.platformplayer.logging.Logger import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -34,10 +35,14 @@ class SimpleOrientationListener( lastStableOrientation = newOrientation lifecycleScope.launch(Dispatchers.Main) { - delay(stabilityThresholdTime) - if (newOrientation == lastStableOrientation) { - lastOrientation = newOrientation - onOrientationChanged.emit(newOrientation) + try { + delay(stabilityThresholdTime) + if (newOrientation == lastStableOrientation) { + lastOrientation = newOrientation + onOrientationChanged.emit(newOrientation) + } + } catch (e: Throwable) { + Logger.i(TAG, "Failed to trigger onOrientationChanged", e) } } } @@ -52,4 +57,8 @@ class SimpleOrientationListener( fun stopListening() { orientationListener.disable() } + + companion object { + private val TAG = "SimpleOrientationListener" + } } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/Utility.kt b/app/src/main/java/com/futo/platformplayer/Utility.kt index 7476c600..6e2dc7b8 100644 --- a/app/src/main/java/com/futo/platformplayer/Utility.kt +++ b/app/src/main/java/com/futo/platformplayer/Utility.kt @@ -147,8 +147,6 @@ fun InputStream.copyToOutputStream(inputStreamLength: Long, outputStream: Output @Suppress("DEPRECATION") fun Activity.setNavigationBarColorAndIcons() { window.navigationBarColor = ContextCompat.getColor(this, android.R.color.black); - if (Settings.instance.playback.allowVideoToGoUnderCutout) - window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { window.insetsController?.setSystemBarsAppearance(0, WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS); diff --git a/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt b/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt index 8b5fa562..bd998d38 100644 --- a/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt +++ b/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt @@ -14,6 +14,7 @@ import android.os.StrictMode.VmPolicy import android.util.Log import android.util.TypedValue import android.view.View +import android.view.WindowManager import android.widget.FrameLayout import android.widget.ImageView import androidx.activity.result.ActivityResult @@ -112,7 +113,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { private lateinit var _overlayContainer: FrameLayout; private lateinit var _toastView: ToastView; - private lateinit var _multicastLock: WifiManager.MulticastLock //Segment Containers private lateinit var _fragContainerTopBar: FragmentContainerView; @@ -247,12 +247,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { } override fun onCreate(savedInstanceState: Bundle?) { - Logger.i(TAG, "Acquiring multicast lock") - val wifiManager = applicationContext.getSystemService(WIFI_SERVICE) as WifiManager - _multicastLock = wifiManager.createMulticastLock("mdnsLock") - _multicastLock.setReferenceCounted(true) - _multicastLock.acquire() - Logger.i(TAG, "MainActivity Starting"); StateApp.instance.setGlobalContext(this, lifecycleScope); StateApp.instance.mainAppStarting(this); @@ -260,7 +254,8 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setNavigationBarColorAndIcons(); - + if (Settings.instance.playback.allowVideoToGoUnderCutout) + window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES runBlocking { StatePlatform.instance.updateAvailableClients(this@MainActivity); @@ -977,7 +972,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { override fun onDestroy() { super.onDestroy(); Logger.v(TAG, "onDestroy") - _multicastLock.release() StateApp.instance.mainAppDestroyed(this); } diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt index bb41c48e..f9c0e280 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/VideoDetailFragment.kt @@ -2,6 +2,7 @@ package com.futo.platformplayer.fragment.mainactivity.main import android.content.pm.ActivityInfo import android.content.res.Configuration +import android.os.Build import android.os.Bundle import android.os.Handler import android.util.Log @@ -10,6 +11,7 @@ import android.view.View import android.view.ViewGroup import android.view.WindowInsets import android.view.WindowInsetsController +import android.view.WindowManager import androidx.constraintlayout.motion.widget.MotionLayout import androidx.core.view.WindowCompat import androidx.lifecycle.lifecycleScope @@ -426,22 +428,42 @@ class VideoDetailFragment : MainFragment { onMaximized.clear(); } - private fun hideSystemUI() { - WindowCompat.setDecorFitsSystemWindows(requireActivity().window, false) - activity?.window?.insetsController?.let { controller -> - controller.hide(WindowInsets.Type.statusBars()) - controller.hide(WindowInsets.Type.systemBars()) - controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + WindowCompat.setDecorFitsSystemWindows(requireActivity().window, false) + activity?.window?.insetsController?.let { controller -> + controller.hide(WindowInsets.Type.statusBars()) + controller.hide(WindowInsets.Type.systemBars()) + controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + } + } else { + @Suppress("DEPRECATION") + activity?.window?.setFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN + ) + @Suppress("DEPRECATION") + activity?.window?.decorView?.systemUiVisibility = ( + View.SYSTEM_UI_FLAG_FULLSCREEN + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + ) } } private fun showSystemUI() { - WindowCompat.setDecorFitsSystemWindows(requireActivity().window, true) - activity?.window?.insetsController?.let { controller -> - controller.show(WindowInsets.Type.statusBars()) - controller.show(WindowInsets.Type.systemBars()) - controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_DEFAULT + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + WindowCompat.setDecorFitsSystemWindows(requireActivity().window, true) + activity?.window?.insetsController?.let { controller -> + controller.show(WindowInsets.Type.statusBars()) + controller.show(WindowInsets.Type.systemBars()) + controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_DEFAULT + } + } else { + @Suppress("DEPRECATION") + activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN) + @Suppress("DEPRECATION") + activity?.window?.decorView?.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE } } diff --git a/app/src/main/java/com/futo/platformplayer/mdns/MDNSListener.kt b/app/src/main/java/com/futo/platformplayer/mdns/MDNSListener.kt index 91e8ee95..70268ec0 100644 --- a/app/src/main/java/com/futo/platformplayer/mdns/MDNSListener.kt +++ b/app/src/main/java/com/futo/platformplayer/mdns/MDNSListener.kt @@ -232,7 +232,7 @@ class MDNSListener { private fun receiveLoop(client: DatagramSocket) { Logger.i(TAG, "Started receive loop") - val buffer = ByteArray(1024) + val buffer = ByteArray(8972) val packet = DatagramPacket(buffer, buffer.size) while (_started) { try { diff --git a/app/src/main/java/com/futo/platformplayer/receivers/MediaControlReceiver.kt b/app/src/main/java/com/futo/platformplayer/receivers/MediaControlReceiver.kt index 61bec334..5540406a 100644 --- a/app/src/main/java/com/futo/platformplayer/receivers/MediaControlReceiver.kt +++ b/app/src/main/java/com/futo/platformplayer/receivers/MediaControlReceiver.kt @@ -10,7 +10,6 @@ import com.futo.platformplayer.constructs.Event1 class MediaControlReceiver : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { val act = intent?.getStringExtra(EXTRA_MEDIA_ACTION); Logger.i(TAG, "Received MediaControl Event $act"); diff --git a/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt b/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt index e12e24af..180c9ea8 100644 --- a/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt +++ b/app/src/main/java/com/futo/platformplayer/services/MediaPlaybackService.kt @@ -55,9 +55,15 @@ class MediaPlaybackService : Service() { private var _notificationChannel: NotificationChannel? = null; private var _mediaSession: MediaSessionCompat? = null; private var _hasFocus: Boolean = false; + private var _isTransientLoss: Boolean = false; private var _focusRequest: AudioFocusRequest? = null; private var _audioFocusLossTime_ms: Long? = null private var _playbackState = PlaybackStateCompat.STATE_NONE; + private var _lastAudioFocusAttempt_ms: Long? = null + private val isPlaying get() = _playbackState != PlaybackStateCompat.STATE_PAUSED && + _playbackState != PlaybackStateCompat.STATE_STOPPED && + _playbackState != PlaybackStateCompat.STATE_NONE && + _playbackState != PlaybackStateCompat.STATE_ERROR override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Logger.v(TAG, "onStartCommand"); @@ -159,12 +165,7 @@ class MediaPlaybackService : Service() { Logger.v(TAG, "closeMediaSession"); stopForeground(STOP_FOREGROUND_REMOVE); - val focusRequest = _focusRequest; - if (focusRequest != null) { - _audioManager?.abandonAudioFocusRequest(focusRequest); - _focusRequest = null; - } - _hasFocus = false; + abandonAudioFocus() val notifManager = _notificationManager; Logger.i(TAG, "Cancelling playback notification (notifManager: ${notifManager != null})"); @@ -335,29 +336,73 @@ class MediaPlaybackService : Service() { .setState(state, pos, 1f, SystemClock.elapsedRealtime()) .build()); - if(_focusRequest == null) - setAudioFocus(); - _playbackState = state; + try { + setAudioFocus() + } catch (e: Throwable) { + Logger.e(TAG, "Failed to set audio focus", e) + } } //TODO: (TBD) This code probably more fitting inside FutoVideoPlayer, as this service is generally only used for global events private fun setAudioFocus() { - Log.i(TAG, "Requested audio focus."); + if (!isPlaying) { + return + } - val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) - .setAcceptsDelayedFocusGain(true) - .setOnAudioFocusChangeListener(_audioFocusChangeListener) - .build() + if (_hasFocus || _isTransientLoss) { + return; + } - _focusRequest = focusRequest; - val result = _audioManager?.requestAudioFocus(focusRequest) + val now = System.currentTimeMillis() + val lastAudioFocusAttempt_ms = _lastAudioFocusAttempt_ms + if (lastAudioFocusAttempt_ms == null || now - lastAudioFocusAttempt_ms > 1000) { + _lastAudioFocusAttempt_ms = now + } else { + Log.v(TAG, "Skipped trying to get audio focus because gaining audio focus was recently attempted."); + return + } + + if (_focusRequest == null) { + val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) + .setAcceptsDelayedFocusGain(true) + .setOnAudioFocusChangeListener(_audioFocusChangeListener) + .build() + + _focusRequest = focusRequest; + Log.i(TAG, "Created audio focus request."); + } + + Log.i(TAG, "Requesting audio focus."); + + val result = _audioManager?.requestAudioFocus(_focusRequest!!) Log.i(TAG, "Audio focus request result $result"); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { - //TODO: Handle when not possible to get audio focus - _hasFocus = true; + _hasFocus = true + _isTransientLoss = false Log.i(TAG, "Audio focus received"); + } else if (result == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) { + _hasFocus = false + _isTransientLoss = false + Log.i(TAG, "Audio focus delayed, waiting for focus") + } else { + _hasFocus = false + _isTransientLoss = false + Log.i(TAG, "Audio focus not granted, retrying later") } + + Log.i(TAG, "Audio focus requested."); + } + + private fun abandonAudioFocus() { + val focusRequest = _focusRequest; + if (focusRequest != null) { + Logger.i(TAG, "Audio focus abandoned") + _audioManager?.abandonAudioFocusRequest(focusRequest); + _focusRequest = null; + } + _hasFocus = false; + _isTransientLoss = false; } private val _audioFocusChangeListener = @@ -365,9 +410,8 @@ class MediaPlaybackService : Service() { try { when (focusChange) { AudioManager.AUDIOFOCUS_GAIN -> { - //Do not start playing on gaining audo focus - //MediaControlReceiver.onPlayReceived.emit(); _hasFocus = true; + _isTransientLoss = false; Log.i(TAG, "Audio focus gained (restartPlaybackAfterLoss = ${Settings.instance.playback.restartPlaybackAfterLoss}, _audioFocusLossTime_ms = $_audioFocusLossTime_ms)"); if (Settings.instance.playback.restartPlaybackAfterLoss == 1) { @@ -385,40 +429,28 @@ class MediaPlaybackService : Service() { } } AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> { - MediaControlReceiver.onPauseReceived.emit(); - if (_playbackState != PlaybackStateCompat.STATE_PAUSED && - _playbackState != PlaybackStateCompat.STATE_STOPPED && - _playbackState != PlaybackStateCompat.STATE_NONE && - _playbackState != PlaybackStateCompat.STATE_ERROR) { - _audioFocusLossTime_ms = System.currentTimeMillis() - } - - Log.i(TAG, "Audio focus transient loss"); - } - AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> { - Log.i(TAG, "Audio focus transient loss, can duck"); - } - AudioManager.AUDIOFOCUS_LOSS -> { - if (_playbackState != PlaybackStateCompat.STATE_PAUSED && - _playbackState != PlaybackStateCompat.STATE_STOPPED && - _playbackState != PlaybackStateCompat.STATE_NONE && - _playbackState != PlaybackStateCompat.STATE_ERROR) { + if (isPlaying) { _audioFocusLossTime_ms = System.currentTimeMillis() } _hasFocus = false; + _isTransientLoss = true; MediaControlReceiver.onPauseReceived.emit(); - Log.i(TAG, "Audio focus lost"); - - val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager - val runningAppProcesses = activityManager.runningAppProcesses - for (processInfo in runningAppProcesses) { - // Check the importance of the running app process - if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { - // This app is in the foreground, which might have caused the loss of audio focus - Log.i("AudioFocus", "App ${processInfo.processName} might have caused the loss of audio focus") - } + Log.i(TAG, "Audio focus transient loss (_audioFocusLossTime_ms = ${_audioFocusLossTime_ms})"); + } + AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> { + Log.i(TAG, "Audio focus transient loss, can duck"); + _hasFocus = true; + _isTransientLoss = true; + } + AudioManager.AUDIOFOCUS_LOSS -> { + if (isPlaying) { + _audioFocusLossTime_ms = System.currentTimeMillis() } + + MediaControlReceiver.onPauseReceived.emit(); + abandonAudioFocus(); + Log.i(TAG, "Audio focus lost"); } } } catch(ex: Throwable) { diff --git a/app/src/stable/assets/sources/bilibili b/app/src/stable/assets/sources/bilibili index 850acb49..31490e10 160000 --- a/app/src/stable/assets/sources/bilibili +++ b/app/src/stable/assets/sources/bilibili @@ -1 +1 @@ -Subproject commit 850acb49a8d988f45b893ef848d559dd74d0db1f +Subproject commit 31490e10f9ef661d6365c0cf4d0fcedf9d807a69 diff --git a/app/src/unstable/assets/sources/bilibili b/app/src/unstable/assets/sources/bilibili index 850acb49..31490e10 160000 --- a/app/src/unstable/assets/sources/bilibili +++ b/app/src/unstable/assets/sources/bilibili @@ -1 +1 @@ -Subproject commit 850acb49a8d988f45b893ef848d559dd74d0db1f +Subproject commit 31490e10f9ef661d6365c0cf4d0fcedf9d807a69