Browse Source

Merge pull request 'Player PiP' (#24) from feature/pip_activity into develop

Reviewed-on: #24
pull/28/head
Jannik 1 year ago
parent
commit
8c0f4965e7
  1. 15
      app/src/main/AndroidManifest.xml
  2. 3
      app/src/main/java/org/mosad/teapod/MainActivity.kt
  3. 123
      app/src/main/java/org/mosad/teapod/player/PlayerActivity.kt
  4. 57
      app/src/main/java/org/mosad/teapod/util/ActivityUtils.kt

15
app/src/main/AndroidManifest.xml

@ -21,16 +21,21 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".player.PlayerActivity"
android:label="@string/app_name"
android:theme="@style/PlayerTheme"
android:configChanges="orientation|screenSize|layoutDirection" />
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".player.PlayerActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|layoutDirection"
android:excludeFromRecents="true"
android:label="@string/app_name"
android:launchMode="singleTask"
android:parentActivityName=".MainActivity"
android:supportsPictureInPicture="true"
android:taskAffinity=".player.PlayerActivity"
android:theme="@style/PlayerTheme" />
</application>
</manifest>

3
app/src/main/java/org/mosad/teapod/MainActivity.kt

@ -165,6 +165,9 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
}
}
/**
* start the player as new activity
*/
fun startPlayer(mediaId: Int, episodeId: Int) {
val intent = Intent(this, PlayerActivity::class.java).apply {
putExtra(getString(R.string.intent_media_id), mediaId)

123
app/src/main/java/org/mosad/teapod/player/PlayerActivity.kt

@ -3,11 +3,19 @@ package org.mosad.teapod.player
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.annotation.SuppressLint
import android.app.PictureInPictureParams
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.*
import android.util.Rational
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import androidx.activity.viewModels
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.GestureDetectorCompat
import androidx.core.view.isVisible
@ -26,6 +34,9 @@ import org.mosad.teapod.preferences.Preferences
import org.mosad.teapod.ui.components.EpisodesListPlayer
import org.mosad.teapod.ui.components.LanguageSettingsPlayer
import org.mosad.teapod.util.DataTypes
import org.mosad.teapod.util.hideBars
import org.mosad.teapod.util.isInPiPMode
import org.mosad.teapod.util.navToLauncherTask
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.concurrent.scheduleAtFixedRate
@ -38,6 +49,7 @@ class PlayerActivity : AppCompatActivity() {
private lateinit var gestureDetector: GestureDetectorCompat
private lateinit var timerUpdates: TimerTask
private var wasInPiP = false
private var playWhenReady = true
private var currentWindow = 0
private var playbackPosition: Long = 0
@ -73,6 +85,11 @@ class PlayerActivity : AppCompatActivity() {
initActions()
}
/**
* once minimum is android 7.0 this can be simplified
* only onStart and onStop should be needed then
* see: https://developer.android.com/guide/topics/ui/picture-in-picture#continuing_playback
*/
override fun onStart() {
super.onStart()
if (Util.SDK_INT > 23) {
@ -83,6 +100,8 @@ class PlayerActivity : AppCompatActivity() {
override fun onResume() {
super.onResume()
if (isInPiPMode()) { return }
if (Util.SDK_INT <= 23) {
initPlayer()
video_view?.onResume()
@ -91,6 +110,8 @@ class PlayerActivity : AppCompatActivity() {
override fun onPause() {
super.onPause()
if (isInPiPMode()) { return }
if (Util.SDK_INT <= 23) {
video_view?.onPause()
releasePlayer()
@ -103,6 +124,9 @@ class PlayerActivity : AppCompatActivity() {
video_view?.onPause()
releasePlayer()
}
// if the player was in pip, it's on a different task
if (wasInPiP) { navToLauncherTask() }
}
override fun onSaveInstanceState(outState: Bundle) {
@ -112,6 +136,59 @@ class PlayerActivity : AppCompatActivity() {
super.onSaveInstanceState(outState)
}
/**
* used, when the player is in pip and the user selects a new media
*/
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
// when the intent changed, lead the new media and play it
intent?.let {
model.loadMedia(
it.getIntExtra(getString(R.string.intent_media_id), 0),
it.getIntExtra(getString(R.string.intent_episode_id), 0)
)
model.playEpisode(model.currentEpisode, replace = true)
}
}
/**
* previous to android n, don't override
*/
@RequiresApi(Build.VERSION_CODES.N)
override fun onUserLeaveHint() {
super.onUserLeaveHint()
// start pip mode, if supported
if (packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
@Suppress("deprecation")
enterPictureInPictureMode()
} else {
val width = model.player.videoFormat?.width ?: 0
val height = model.player.videoFormat?.height ?: 0
val params = PictureInPictureParams.Builder()
.setAspectRatio(Rational(width, height))
.build()
enterPictureInPictureMode(params)
}
wasInPiP = isInPiPMode()
}
}
override fun onPictureInPictureModeChanged(
isInPictureInPictureMode: Boolean,
newConfig: Configuration?
) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
// Hide the full-screen UI (controls, etc.) while in picture-in-picture mode.
if (isInPictureInPictureMode) {
controller.hideImmediately()
}
}
private fun initPlayer() {
if (model.media.id < 0) {
Log.e(javaClass.name, "No media was set.")
@ -178,7 +255,9 @@ class PlayerActivity : AppCompatActivity() {
}
private fun initActions() {
exo_close_player.setOnClickListener { this.finish() }
exo_close_player.setOnClickListener {
this.finish()
}
rwd_10.setOnButtonClickListener { rewind() }
ffwd_10.setOnButtonClickListener { fastForward() }
button_next_ep.setOnClickListener { playNextEpisode() }
@ -213,8 +292,8 @@ class PlayerActivity : AppCompatActivity() {
}
if (remainingTime in 1..20000) {
// if the next ep button is not visible, make it visible
if (!btnNextEpIsVisible && model.nextEpisode != null && Preferences.autoplay) {
// if the next ep button is not visible, make it visible. Don't show in pip mode
if (!btnNextEpIsVisible && model.nextEpisode != null && Preferences.autoplay && !isInPiPMode()) {
withContext(Dispatchers.Main) { showButtonNextEp() }
}
} else if (btnNextEpIsVisible) {
@ -229,7 +308,7 @@ class PlayerActivity : AppCompatActivity() {
}
}
private fun releasePlayer(){
private fun releasePlayer() {
playbackPosition = model.player.currentPosition
currentWindow = model.player.currentWindowIndex
playWhenReady = model.player.playWhenReady
@ -260,11 +339,19 @@ class PlayerActivity : AppCompatActivity() {
private fun onMediaChanged() {
exo_text_title.text = model.getMediaTitle()
// hide the next ep button, if there is none
button_next_ep_c.visibility = if (model.nextEpisode == null) {
View.GONE
} else {
View.VISIBLE
}
// hide the episodes button, if the media type changed
button_episodes.visibility = if (model.media.type == DataTypes.MediaType.MOVIE) {
View.GONE
} else {
View.VISIBLE
}
}
/**
@ -312,27 +399,6 @@ class PlayerActivity : AppCompatActivity() {
hideButtonNextEp()
}
/**
* hide the status and navigation bar
*/
private fun hideBars() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.setDecorFitsSystemWindows(false)
window.insetsController?.apply {
hide(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE
}
} else {
@Suppress("deprecation")
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN)
}
}
/**
* show the next episode button
* TODO improve the show animation
@ -393,7 +459,10 @@ class PlayerActivity : AppCompatActivity() {
* on single tap hide or show the controls
*/
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
if (controller.isVisible) controller.hide() else controller.show()
if (!isInPiPMode()) {
if (controller.isVisible) controller.hide() else controller.show()
}
return true
}

57
app/src/main/java/org/mosad/teapod/util/ActivityUtils.kt

@ -0,0 +1,57 @@
package org.mosad.teapod.util
import android.app.Activity
import android.app.ActivityManager
import android.content.Context
import android.content.Intent
import android.os.Build
import android.view.View
import android.view.WindowInsets
import android.view.WindowInsetsController
/**
* hide the status and navigation bar
*/
fun Activity.hideBars() {
window.apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
setDecorFitsSystemWindows(false)
insetsController?.apply {
hide(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE
}
} else {
@Suppress("deprecation")
decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN)
}
}
}
fun Activity.isInPiPMode(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
isInPictureInPictureMode
} else {
false // pip mode not supported
}
}
/**
* Bring up launcher task to front
*/
fun Activity.navToLauncherTask() {
val activityManager = (getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager)
activityManager.appTasks.forEach { task ->
val baseIntent = task.taskInfo.baseIntent
val categories = baseIntent.categories
if (categories != null && categories.contains(Intent.CATEGORY_LAUNCHER)) {
task.moveToFront()
return
}
}
}
Loading…
Cancel
Save

Du besuchst diese Seite mit einem veralteten IPv4-Internetzugang. Möglicherweise treten in Zukunft Probleme mit der Erreichbarkeit und Performance auf. Bitte frage deinen Internetanbieter oder Netzwerkadministrator nach IPv6-Unterstützung.
You are visiting this site with an outdated IPv4 internet access. You may experience problems with accessibility and performance in the future. Please ask your ISP or network administrator for IPv6 support.
Weitere Infos | More Information
Klicke zum schließen | Click to close