player stuff

This commit is contained in:
LagradOst 2021-06-11 01:00:22 +02:00
parent 47e3876e4a
commit a102d5f66d
15 changed files with 338 additions and 38 deletions

View File

@ -1,26 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lagradost.cloudstream3">
xmlns:tools="http://schemas.android.com/tools" package="com.lagradost.cloudstream3">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:usesCleartextTraffic="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
android:theme="@style/AppTheme" android:fullBackupContent="@xml/backup_descriptor">
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.lagradost.cloudstream3.utils.CastOptionsProvider"/>
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden|navigation"
android:label="@string/app_name">
android:label="@string/app_name"
android:resizeableActivity="true"
android:supportsPictureInPicture="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@ -41,5 +44,4 @@
android:resource="@xml/provider_paths"/>
</provider>
</application>
</manifest>

View File

@ -1,24 +1,58 @@
package com.lagradost.cloudstream3
import android.app.PictureInPictureParams
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import com.google.android.material.bottomnavigation.BottomNavigationView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContentProviderCompat.requireContext
import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupWithNavController
import com.lagradost.cloudstream3.UIHelper.checkWrite
import com.lagradost.cloudstream3.UIHelper.hasPIPPermission
import com.lagradost.cloudstream3.UIHelper.requestRW
import com.lagradost.cloudstream3.UIHelper.shouldShowPIPMode
class MainActivity : AppCompatActivity() {/*, ViewModelStoreOwner {
private val appViewModelStore: ViewModelStore by lazy {
ViewModelStore()
class MainActivity : AppCompatActivity() {
/*, ViewModelStoreOwner {
private val appViewModelStore: ViewModelStore by lazy {
ViewModelStore()
}
override fun getViewModelStore(): ViewModelStore {
return appViewModelStore
}*/
companion object {
var isInPlayer: Boolean = false
var canShowPipMode: Boolean = false
var isInPIPMode: Boolean = false
}
override fun getViewModelStore(): ViewModelStore {
return appViewModelStore
}*/
private fun enterPIPMode() {
if (!shouldShowPIPMode(isInPlayer) || !canShowPipMode) return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
enterPictureInPictureMode(PictureInPictureParams.Builder().build())
} catch (e: Exception) {
enterPictureInPictureMode()
}
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
enterPictureInPictureMode()
}
}
}
override fun onUserLeaveHint() {
super.onUserLeaveHint()
if (isInPlayer && canShowPipMode) {
enterPIPMode()
}
}
private fun AppCompatActivity.backPressed(): Boolean {
val currentFragment = supportFragmentManager.fragments.last {
@ -46,6 +80,13 @@ class MainActivity : AppCompatActivity() {/*, ViewModelStoreOwner {
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
//https://stackoverflow.com/questions/52594181/how-to-know-if-user-has-disabled-picture-in-picture-feature-permission
//https://developer.android.com/guide/topics/ui/picture-in-picture
canShowPipMode =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && // OS SUPPORT
packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) && // HAS FEATURE, MIGHT BE BLOCKED DUE TO POWER DRAIN
hasPIPPermission() // CHECK IF FEATURE IS ENABLED IN SETTINGS
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.

View File

@ -2,6 +2,7 @@ package com.lagradost.cloudstream3
import android.Manifest
import android.app.Activity
import android.app.AppOpsManager
import android.content.Context
import android.content.pm.PackageManager
import android.content.res.Resources
@ -11,6 +12,7 @@ import android.media.AudioManager
import android.os.Build
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
@ -19,6 +21,7 @@ import androidx.preference.PreferenceManager
import com.google.android.gms.cast.framework.CastContext
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.lagradost.cloudstream3.UIHelper.getGridFormat
import com.lagradost.cloudstream3.ui.result.ResultFragment
import com.lagradost.cloudstream3.utils.Event
@ -261,4 +264,24 @@ object UIHelper {
) // or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
// window.clearFlags(View.KEEP_SCREEN_ON)
}
fun Context.shouldShowPIPMode(isInPlayer: Boolean): Boolean {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
return settingsManager?.getBoolean("pip_enabled", true) ?: true && isInPlayer
}
fun Context.hasPIPPermission(): Boolean {
val appOps =
getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
return appOps.checkOpNoThrow(
AppOpsManager.OPSTR_PICTURE_IN_PICTURE,
android.os.Process.myUid(),
packageName
) == AppOpsManager.MODE_ALLOWED
}
fun Context.hideKeyboard(view: View) {
val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
}
}

View File

@ -3,22 +3,28 @@ package com.lagradost.cloudstream3.ui.player
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.app.PendingIntent
import android.app.PictureInPictureParams
import android.app.RemoteAction
import android.content.*
import android.content.Context.AUDIO_SERVICE
import android.content.SharedPreferences
import android.content.pm.ActivityInfo
import android.content.res.Resources
import android.database.ContentObserver
import android.graphics.Color
import android.graphics.drawable.Icon
import android.media.AudioManager
import android.net.Uri
import android.os.*
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.View.*
import android.view.ViewGroup
import android.view.animation.*
import android.view.animation.AccelerateInterpolator
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.ProgressBar
import android.widget.Toast
import android.widget.Toast.LENGTH_SHORT
@ -26,7 +32,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.mediarouter.app.MediaRouteButton
import androidx.preference.PreferenceManager
import androidx.transition.Fade
import androidx.transition.Transition
@ -44,6 +49,7 @@ import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory
import com.google.android.exoplayer2.util.MimeTypes
import com.google.android.exoplayer2.util.Util
import com.google.android.gms.cast.MediaInfo
import com.google.android.gms.cast.MediaMetadata
import com.google.android.gms.cast.MediaQueueItem
@ -53,10 +59,13 @@ import com.google.android.gms.cast.framework.CastContext
import com.google.android.gms.cast.framework.CastState
import com.google.android.gms.common.images.WebImage
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.MainActivity.Companion.isInPIPMode
import com.lagradost.cloudstream3.MainActivity.Companion.isInPlayer
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.UIHelper.getFocusRequest
import com.lagradost.cloudstream3.UIHelper.getNavigationBarHeight
import com.lagradost.cloudstream3.UIHelper.getStatusBarHeight
import com.lagradost.cloudstream3.UIHelper.hideKeyboard
import com.lagradost.cloudstream3.UIHelper.hideSystemUI
import com.lagradost.cloudstream3.UIHelper.isCastApiAvailable
import com.lagradost.cloudstream3.UIHelper.popCurrentPage
@ -72,12 +81,10 @@ import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.getId
import kotlinx.android.synthetic.main.fragment_player.*
import kotlinx.android.synthetic.main.fragment_result.*
import kotlinx.android.synthetic.main.player_custom_layout.*
import kotlinx.coroutines.*
import org.json.JSONObject
import java.io.File
import java.util.*
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSession
@ -129,12 +136,12 @@ data class PlayerData(
)
class PlayerFragment : Fragment() {
private var isCurrentlyPlaying: Boolean = false
private val mapper = JsonMapper.builder().addModule(KotlinModule())
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()
private var isFullscreen = false
private var isPlayerPlaying = true
private var doubleTapEnabled = false
private lateinit var viewModel: ResultViewModel
private lateinit var playerData: PlayerData
private var isLoading = true
@ -281,20 +288,21 @@ class PlayerFragment : Fragment() {
)
}
fun skipOP() {
private fun skipOP() {
seekTime(85000L)
}
private var swipeEnabled = true //<settingsManager!!.getBoolean("swipe_enabled", true)
private var swipeVerticalEnabled = true//settingsManager.getBoolean("swipe_vertical_enabled", true)
private var playBackSpeedEnabled = true//settingsManager!!.getBoolean("playback_speed_enabled", false)
private var playerResizeEnabled = true//settingsManager!!.getBoolean("player_resize_enabled", false)
private var doubleTapEnabled = false
private var skipTime = 0L
private var prevDiffX = 0.0
private var preventHorizontalSwipe = false
private var hasPassedVerticalSwipeThreshold = false
private var hasPassedSkipLimit = false
private val swipeEnabled = true //<settingsManager!!.getBoolean("swipe_enabled", true)
private val swipeVerticalEnabled = true//settingsManager.getBoolean("swipe_vertical_enabled", true)
private val playBackSpeedEnabled = true//settingsManager!!.getBoolean("playback_speed_enabled", false)
private val playerResizeEnabled = true//settingsManager!!.getBoolean("player_resize_enabled", false)
private val swipeEdgeSize = 10.toPx
private var isMovingStartTime = 0L
private var currentX = 0F
@ -499,6 +507,7 @@ class PlayerFragment : Fragment() {
private var hasUsedFirstRender = false
private fun releasePlayer() {
isCurrentlyPlaying = false
val alphaAnimation = AlphaAnimation(0f, 1f)
alphaAnimation.duration = 100
alphaAnimation.fillAfter = true
@ -563,7 +572,7 @@ class PlayerFragment : Fragment() {
video_locked_img.setColorFilter(color)
val isClick = !isLocked
println("UPDATED LOCK $isClick")
exo_play.isClickable = isClick
exo_pause.isClickable = isClick
exo_ffwd.isClickable = isClick
@ -597,10 +606,109 @@ class PlayerFragment : Fragment() {
private var episodes: ArrayList<ResultEpisode> = ArrayList()
var currentPoster: String? = null
//region PIP MODE
private fun getPen(code: PlayerEventType): PendingIntent {
return getPen(code.value)
}
private fun getPen(code: Int): PendingIntent {
return PendingIntent.getBroadcast(
activity,
code,
Intent("media_control").putExtra("control_type", code),
0
)
}
@SuppressLint("NewApi")
private fun getRemoteAction(id: Int, title: String, event: PlayerEventType): RemoteAction {
return RemoteAction(
Icon.createWithResource(activity, id),
title,
title,
getPen(event)
)
}
@SuppressLint("NewApi")
private fun updatePIPModeActions() {
if (!isInPIPMode || !this::exoPlayer.isInitialized) return
val actions: ArrayList<RemoteAction> = ArrayList()
actions.add(getRemoteAction(R.drawable.go_back_30, "Go Back", PlayerEventType.SeekBack))
if (exoPlayer.isPlaying) {
actions.add(getRemoteAction(R.drawable.netflix_pause, "Pause", PlayerEventType.Pause))
} else {
actions.add(getRemoteAction(R.drawable.ic_baseline_play_arrow_24, "Play", PlayerEventType.Play))
}
actions.add(getRemoteAction(R.drawable.go_forward_30, "Go Forward", PlayerEventType.SeekForward))
activity?.setPictureInPictureParams(PictureInPictureParams.Builder().setActions(actions).build())
}
private var receiver: BroadcastReceiver? = null
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {
isInPIPMode = isInPictureInPictureMode
if (isInPictureInPictureMode) {
// Hide the full-screen UI (controls, etc.) while in picture-in-picture mode.
player_holder.alpha = 0f
receiver = object : BroadcastReceiver() {
override fun onReceive(
context: Context,
intent: Intent,
) {
if (ACTION_MEDIA_CONTROL != intent.action) {
return
}
handlePlayerEvent(intent.getIntExtra(EXTRA_CONTROL_TYPE, 0))
}
}
val filter = IntentFilter()
filter.addAction(
ACTION_MEDIA_CONTROL
)
activity?.registerReceiver(receiver, filter)
updatePIPModeActions()
} else {
// Restore the full-screen UI.
player_holder.alpha = 1f
receiver?.let {
activity?.unregisterReceiver(it)
}
activity?.hideSystemUI()
this.view?.let { activity?.hideKeyboard(it) }
}
}
private fun handlePlayerEvent(event: PlayerEventType) {
handlePlayerEvent(event.value)
}
private fun handlePlayerEvent(event: Int) {
when (event) {
PlayerEventType.Play.value -> exoPlayer.play()
PlayerEventType.Pause.value -> exoPlayer.pause()
PlayerEventType.SeekBack.value -> seekTime(-30000L)
PlayerEventType.SeekForward.value -> seekTime(30000L)
}
}
//endregion
@SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
settingsManager = PreferenceManager.getDefaultSharedPreferences(activity)
swipeEnabled = settingsManager.getBoolean("swipe_enabled", true)
swipeVerticalEnabled = settingsManager.getBoolean("swipe_vertical_enabled", true)
playBackSpeedEnabled = settingsManager.getBoolean("playback_speed_enabled", false)
playerResizeEnabled = settingsManager.getBoolean("player_resize_enabled", true)
doubleTapEnabled = settingsManager.getBoolean("double_tap_enabled", false)
isInPlayer = true // NEED REFERENCE TO MAIN ACTIVITY FOR PIP
navigationBarHeight = requireContext().getNavigationBarHeight()
statusBarHeight = requireContext().getStatusBarHeight()
@ -723,9 +831,6 @@ class PlayerFragment : Fragment() {
}
}
println(episodes)
settingsManager = PreferenceManager.getDefaultSharedPreferences(activity)
val fastForwardTime = settingsManager.getInt("fast_forward_button_time", 10)
exo_rew_text.text = fastForwardTime.toString()
exo_ffwd_text.text = fastForwardTime.toString()
@ -928,6 +1033,8 @@ class PlayerFragment : Fragment() {
}
changeSkip()
initPlayer()
}
private fun getCurrentUrl(): ExtractorLink? {
@ -1003,24 +1110,28 @@ class PlayerFragment : Fragment() {
override fun onStart() {
super.onStart()
thread {
// initPlayer()
if (player_view != null) player_view.onResume()
if (!isCurrentlyPlaying) {
initPlayer()
}
if (player_view != null) player_view.onResume()
}
override fun onResume() {
super.onResume()
activity?.hideSystemUI()
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
thread {
initPlayer()
if (Util.SDK_INT <= 23) {
if (!isCurrentlyPlaying) {
initPlayer()
}
if (player_view != null) player_view.onResume()
}
}
//TODO FIX NON PIP MODE BUG
override fun onDestroy() {
super.onDestroy()
isInPlayer = false
// releasePlayer()
activity?.showSystemUI()
@ -1029,14 +1140,18 @@ class PlayerFragment : Fragment() {
override fun onPause() {
super.onPause()
if (player_view != null) player_view.onPause()
releasePlayer()
if (Util.SDK_INT <= 23) {
if (player_view != null) player_view.onPause()
releasePlayer()
}
}
override fun onStop() {
super.onStop()
if (player_view != null) player_view.onPause()
releasePlayer()
if (Util.SDK_INT > 23) {
if (player_view != null) player_view.onPause()
releasePlayer()
}
}
override fun onSaveInstanceState(outState: Bundle) {
@ -1229,7 +1344,7 @@ class PlayerFragment : Fragment() {
}
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
// updatePIPModeActions()
updatePIPModeActions()
if (activity == null) return
if (playWhenReady) {
when (playbackState) {
@ -1296,6 +1411,7 @@ class PlayerFragment : Fragment() {
//http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4
@SuppressLint("SetTextI18n")
private fun initPlayer() {
isCurrentlyPlaying = true
println("INIT PLAYER")
view?.setOnTouchListener { _, _ -> return@setOnTouchListener true } // VERY IMPORTANT https://stackoverflow.com/questions/28818926/prevent-clicking-on-a-button-in-an-activity-while-showing-a-fragment
val tempUrl = getCurrentUrl()

View File

@ -0,0 +1,11 @@
package com.lagradost.cloudstream3.ui.settings
import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat
import com.lagradost.cloudstream3.R
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.settings, rootKey)
}
}

View File

@ -0,0 +1,6 @@
<vector android:height="27.5dp" android:tint="?attr/white"
android:viewportHeight="293" android:viewportWidth="256"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white"
android:pathData="M226.011429,0 L29.9885714,0 C13.4582857,0 0,13.4582857 0,30.1348571 L0,227.913143 C0,244.589714 13.4582857,258.048 29.9885714,258.048 L195.876571,258.048 L188.123429,230.985143 L206.848,248.393143 L224.548571,264.777143 L256,292.571429 L256,30.1348571 C256,13.4582857 242.541714,0 226.011429,0 Z M169.545143,191.049143 C169.545143,191.049143 164.278857,184.758857 159.890286,179.2 C179.053714,173.787429 186.368,161.792 186.368,161.792 C180.370286,165.741714 174.665143,168.521143 169.545143,170.422857 C162.230857,173.494857 155.209143,175.542857 148.333714,176.713143 C134.290286,179.346286 121.417143,178.614857 110.445714,176.566857 C102.107429,174.957714 94.9394286,172.617143 88.9417143,170.276571 C85.5771429,168.96 81.92,167.350857 78.2628571,165.302857 C77.824,165.010286 77.3851429,164.864 76.9462857,164.571429 C76.6537143,164.425143 76.5074286,164.278857 76.3611429,164.132571 C73.728,162.669714 72.2651429,161.645714 72.2651429,161.645714 C72.2651429,161.645714 79.2868571,173.348571 97.8651429,178.907429 C93.4765714,184.466286 88.064,191.049143 88.064,191.049143 C55.7348571,190.025143 43.4468571,168.813714 43.4468571,168.813714 C43.4468571,121.709714 64.512,83.5291429 64.512,83.5291429 C85.5771429,67.7302857 105.618286,68.1691429 105.618286,68.1691429 L107.081143,69.9245714 C80.7497143,77.5314286 68.608,89.088 68.608,89.088 C68.608,89.088 71.8262857,87.3325714 77.2388571,84.8457143 C92.8914286,77.9702857 105.325714,76.0685714 110.445714,75.6297143 C111.323429,75.4834286 112.054857,75.3371429 112.932571,75.3371429 C121.856,74.1668571 131.949714,73.8742857 142.482286,75.0445714 C156.379429,76.6537143 171.300571,80.7497143 186.514286,89.088 C186.514286,89.088 174.957714,78.1165714 150.089143,70.5097143 L152.137143,68.1691429 C152.137143,68.1691429 172.178286,67.7302857 193.243429,83.5291429 C193.243429,83.5291429 214.308571,121.709714 214.308571,168.813714 C214.308571,168.813714 201.874286,190.025143 169.545143,191.049143 Z M101.522286,122.733714 C93.184,122.733714 86.6011429,130.048 86.6011429,138.971429 C86.6011429,147.894857 93.3302857,155.209143 101.522286,155.209143 C109.860571,155.209143 116.443429,147.894857 116.443429,138.971429 C116.589714,130.048 109.860571,122.733714 101.522286,122.733714 M154.916571,122.733714 C146.578286,122.733714 139.995429,130.048 139.995429,138.971429 C139.995429,147.894857 146.724571,155.209143 154.916571,155.209143 C163.254857,155.209143 169.837714,147.894857 169.837714,138.971429 C169.837714,130.048 163.254857,122.733714 154.916571,122.733714"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M21,3L3,3c-1.11,0 -2,0.89 -2,2v12c0,1.1 0.89,2 2,2h5v2h8v-2h5c1.1,0 1.99,-0.9 1.99,-2L23,5c0,-1.11 -0.9,-2 -2,-2zM21,17L3,17L3,5h18v12zM16,11l-7,4L9,7z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,11h-8v6h8v-6zM23,19L23,4.98C23,3.88 22.1,3 21,3L3,3c-1.1,0 -2,0.88 -2,1.98L1,19c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2zM21,19.02L3,19.02L3,4.97h18v14.05z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M9,11.24V7.5C9,6.12 10.12,5 11.5,5S14,6.12 14,7.5v3.74c1.21,-0.81 2,-2.18 2,-3.74C16,5.01 13.99,3 11.5,3S7,5.01 7,7.5C7,9.06 7.79,10.43 9,11.24zM18.84,15.87l-4.54,-2.26c-0.17,-0.07 -0.35,-0.11 -0.54,-0.11H13v-6C13,6.67 12.33,6 11.5,6S10,6.67 10,7.5v10.74c-3.6,-0.76 -3.54,-0.75 -3.67,-0.75c-0.31,0 -0.59,0.13 -0.79,0.33l-0.79,0.8l4.94,4.94C9.96,23.83 10.34,24 10.75,24h6.79c0.75,0 1.33,-0.55 1.44,-1.28l0.75,-5.27c0.01,-0.07 0.02,-0.14 0.02,-0.2C19.75,16.63 19.37,16.09 18.84,15.87z"/>
</vector>

View File

@ -0,0 +1,5 @@
<vector android:tint="?attr/white" xmlns:tools="http://schemas.android.com/tools" android:height="24dp" android:viewportHeight="1000"
android:viewportWidth="1000" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="?attr/white" android:pathData="M455.34,24.62c-61.96,5.16 -121.85,22.37 -176.93,49.91c-80.55,40.62 -140.44,94.66 -190.36,170.73c-35.8,54.39 -60.24,116 -72.29,182.78C7.85,471.07 8.2,555.75 16.11,597.4c33.05,172.45 148.36,312.21 307.73,372.1c26.16,9.98 36.49,10.33 45.44,1.38c6.2,-6.2 6.88,-11.36 6.88,-52.67l0,-45.44l-43.72,-0c-37.86,-0 -46.47,-1.03 -60.24,-7.57c-27.19,-12.39 -45.44,-32.35 -62.65,-68.16c-15.15,-31.67 -34.42,-55.42 -58.17,-72.29c-23.75,-16.87 -5.16,-29.95 28.57,-20.31c14.8,4.48 23.06,9.64 37.86,25.13c10.33,10.67 24.44,26.85 30.98,36.14c7.57,10.67 18.93,20.65 30.98,27.54c16.52,9.29 22.03,10.67 44.75,10.33c14.11,-0.35 31.67,-2.76 38.9,-5.16c12.05,-4.48 13.77,-7.23 20.65,-27.54c4.13,-12.05 11.02,-26.85 15.49,-32.36l7.92,-9.98l-35.46,-7.57c-63.68,-13.43 -99.82,-32.01 -132.52,-68.16c-33.73,-36.83 -52.67,-94.31 -52.67,-160.4c0,-51.63 9.98,-84.68 36.83,-121.17l11.7,-16.18l-4.13,-21c-7.23,-37.52 2.75,-103.96 16.52,-109.12c14.46,-5.51 72.97,15.83 112.21,40.96l16.18,10.33l32.01,-6.2c44.06,-8.95 139.41,-9.29 183.47,-0.34l31.33,6.54l30.29,-17.56c33.73,-19.28 64.71,-30.64 87.43,-32.01l15.49,-1.03l6.54,17.21c8.61,22.03 11.71,70.91 5.85,93.97l-4.13,17.56l15.49,23.41c27.54,41.99 31.67,57.14 31.67,116.35c-0.34,43.37 -1.38,56.11 -7.92,79.17c-19.28,67.12 -60.24,110.84 -125.3,134.59c-8.26,3.1 -30.64,8.95 -50.25,13.08l-35.46,7.57l7.57,9.29c4.13,5.51 10.67,17.9 14.46,27.54c6.88,16.18 7.57,26.16 8.61,107.4c0.69,59.9 2.41,90.88 5.16,93.97c11.71,14.46 27.88,11.71 83.65,-14.8c67.12,-31.32 136.31,-88.12 182.09,-149.39c42.68,-56.45 77.11,-136.31 89.84,-206.88c7.92,-43.72 7.92,-121.51 0,-165.23c-19.96,-110.5 -81.58,-218.24 -165.57,-288.8C716.26,54.91 586.14,12.92 455.34,24.62z"
tools:ignore="VectorPath"/>
</vector>

View File

@ -15,5 +15,9 @@
android:id="@+id/navigation_notifications"
android:icon="@drawable/netflix_download"
android:title="@string/title_downloads"/>
<item
android:id="@+id/navigation_settings"
android:icon="@drawable/netflix_download"
android:title="@string/title_settings"/>
</menu>

View File

@ -22,4 +22,10 @@
android:name="com.lagradost.cloudstream3.ui.notifications.NotificationsFragment"
android:label="@string/title_downloads"
tools:layout="@layout/fragment_notifications"/>
<fragment
android:id="@+id/navigation_settings"
android:layout_height="match_parent"
android:name="com.lagradost.cloudstream3.ui.settings.SettingsFragment"
android:label="@string/title_settings"
/>
</navigation>

View File

@ -3,6 +3,7 @@
<string name="title_home">Home</string>
<string name="title_search">Search</string>
<string name="title_downloads">Downloads</string>
<string name="title_settings">Settings</string>
<string name="search_hint">Search...</string>
<string name="change_providers_descript">Change Providers</string>
<string name="search_providers_list_key">search_providers_list</string>

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
</full-backup-content>

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory/>
<PreferenceCategory
android:key="video"
android:title="Player"
app:isPreferenceVisible="true"
>
<SwitchPreference
android:icon="@drawable/ic_baseline_picture_in_picture_alt_24"
app:key="pip_enabled"
android:title="Picture-in-picture"
android:summary="Continues playback in a miniature player on top of other apps"
app:defaultValue="true"
/>
<SwitchPreference
android:icon="@drawable/ic_baseline_aspect_ratio_24"
app:key="player_resize_enabled"
android:title="Player resize button"
android:summary="Remove the black borders"
app:defaultValue="true"
/>
<SwitchPreference
android:icon="@drawable/ic_baseline_speed_24"
app:key="playback_speed_enabled"
android:title="Eigengrau Mode"
android:summary="Adds the speed option in the player"
app:defaultValue="true"
/>
<SwitchPreference
android:icon="@drawable/ic_baseline_ondemand_video_24"
app:key="swipe_enabled"
android:title="Swipe to seek"
android:summary="Swipe left or right to control time in the videoplayer"
app:defaultValue="true"
/>
<SwitchPreference
android:icon="@drawable/ic_baseline_ondemand_video_24"
app:key="swipe_vertical_enabled"
android:title="Swipe to change settings"
android:summary="Swipe on the left or right side to change brightness or volume"
app:defaultValue="true"
/>
<SwitchPreference
android:icon="@drawable/ic_baseline_touch_app_24"
app:key="double_tap_enabled"
android:title="Double tap to seek"
android:summary="Tap twice on the right or left side to seek forwards or backwards"
app:defaultValue="false"
/>
<Preference
android:title="Github"
android:icon="@drawable/ic_github_logo"
app:summary="https://github.com/LagradOst/CloudStream-3">
<intent android:action="android.intent.action.VIEW"
android:data="https://github.com/LagradOst/CloudStream-3"/>
</Preference>
<Preference
android:title="Join Discord"
android:icon="@drawable/ic_baseline_discord_24"
app:summary="https://discord.gg/5Hus6fM">
<intent android:action="android.intent.action.VIEW"
android:data="https://discord.gg/5Hus6fM"/>
</Preference>
</PreferenceCategory>
</PreferenceScreen>