commit
ac54e40555
44 changed files with 2778 additions and 462 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -10,6 +10,7 @@
|
|||
/.idea/discord.xml
|
||||
/.idea/gradle.xml
|
||||
/.idea/deploymentTargetDropDown.xml
|
||||
/.idea/misc.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DesignSurface">
|
||||
<option name="filePathToZoomLevelMap">
|
||||
<map>
|
||||
<entry key="app/src/main/res/layout/fragment_home.xml" value="0.1736111111111111" />
|
||||
<entry key="app/src/main/res/layout/fragment_library.xml" value="0.1736111111111111" />
|
||||
<entry key="app/src/main/res/layout/fragment_season.xml" value="0.20471014492753623" />
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
21
README.md
21
README.md
|
@ -1,4 +1,4 @@
|
|||

|
||||

|
||||
|
||||
# Findroid
|
||||
|
||||
|
@ -21,12 +21,19 @@ Home | Library | Movie | Season | Episode
|
|||
- Completely native interface
|
||||
- Supported media items: movies, series, seasons, episodes
|
||||
- Direct play only, (no transcoding)
|
||||
- Video codes: H.263, H.264, H.265, VP8, VP9, AV1
|
||||
- Support depends on Android device
|
||||
- Audio codes: Vorbis, Opus, FLAC, ALAC, PCM µ-law, PCM A-law, MP1, MP2, MP3, AMR-NB, AMR-WB, AAC, AC-3, E-AC-3, DTS, DTS-HD, TrueHD
|
||||
- Support provided by ExoPlayer FFmpeg extension
|
||||
- Subtitle codecs: SRT, VTT, SSA/ASS, PGSSUB
|
||||
- SSA/ASS has limited styling support see [this issue](https://github.com/google/ExoPlayer/issues/8435)
|
||||
- ExoPlayer
|
||||
- Video codes: H.263, H.264, H.265, VP8, VP9, AV1
|
||||
- Support depends on Android device
|
||||
- Audio codes: Vorbis, Opus, FLAC, ALAC, PCM µ-law, PCM A-law, MP1, MP2, MP3, AMR-NB, AMR-WB, AAC, AC-3, E-AC-3, DTS, DTS-HD, TrueHD
|
||||
- Support provided by ExoPlayer FFmpeg extension
|
||||
- Subtitle codecs: SRT, VTT, SSA/ASS, PGSSUB
|
||||
- SSA/ASS has limited styling support see [this issue](https://github.com/google/ExoPlayer/issues/8435)
|
||||
- **NEW** MPV Player
|
||||
- Should play everything, including SSA/ASS subs with proper styling!
|
||||
- Optionally force software decoding when hardware decoding has issues.
|
||||
- Issues:
|
||||
- Can only play one item at a time, doesn't transistion to the next episode
|
||||
|
||||
|
||||
## Planned features
|
||||
- Websocket connection (Syncplay)
|
||||
|
|
113
app/build.gradle
113
app/build.gradle
|
@ -1,113 +0,0 @@
|
|||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
id 'kotlin-parcelize'
|
||||
id 'kotlin-kapt'
|
||||
id 'androidx.navigation.safeargs.kotlin'
|
||||
id 'dagger.hilt.android.plugin'
|
||||
id "com.mikepenz.aboutlibraries.plugin"
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
buildToolsVersion "31.0.0"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "dev.jdtech.jellyfin"
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 31
|
||||
versionCode 3
|
||||
versionName "0.1.2"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
coreLibraryDesugaringEnabled true
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
dataBinding true
|
||||
viewBinding true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.core:core-ktx:1.6.0'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.0-alpha01'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
|
||||
// Material
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
|
||||
// ConstraintLayout
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
|
||||
|
||||
// Navigation
|
||||
def navigation_version = "2.3.5"
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
|
||||
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
|
||||
|
||||
// RecyclerView
|
||||
implementation "androidx.recyclerview:recyclerview:1.2.1"
|
||||
implementation "androidx.recyclerview:recyclerview-selection:1.1.0"
|
||||
|
||||
// Room
|
||||
def room_version = "2.3.0"
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
|
||||
// Preference
|
||||
def preference_version = "1.1.1"
|
||||
implementation "androidx.preference:preference-ktx:$preference_version"
|
||||
|
||||
// Jellyfin
|
||||
def jellyfin_version = "1.0.2"
|
||||
implementation "org.jellyfin.sdk:jellyfin-platform-android:$jellyfin_version"
|
||||
|
||||
// Glide
|
||||
def glide_version = "4.12.0"
|
||||
implementation "com.github.bumptech.glide:glide:$glide_version"
|
||||
kapt "com.github.bumptech.glide:compiler:$glide_version"
|
||||
|
||||
// Hilt
|
||||
def hilt_version = "2.38.1"
|
||||
implementation "com.google.dagger:hilt-android:$hilt_version"
|
||||
kapt "com.google.dagger:hilt-compiler:$hilt_version"
|
||||
|
||||
// ExoPlayer
|
||||
def exoplayer_version = "2.15.0"
|
||||
implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
|
||||
implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayer_version"
|
||||
implementation files('libs/extension-ffmpeg-release.aar')
|
||||
|
||||
// Timber
|
||||
def timber_version = "5.0.1"
|
||||
implementation "com.jakewharton.timber:timber:$timber_version"
|
||||
|
||||
def about_libraries_version = "8.9.1"
|
||||
implementation "com.mikepenz:aboutlibraries-core:$about_libraries_version"
|
||||
implementation "com.mikepenz:aboutlibraries:$about_libraries_version"
|
||||
|
||||
// Testing
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
}
|
123
app/build.gradle.kts
Normal file
123
app/build.gradle.kts
Normal file
|
@ -0,0 +1,123 @@
|
|||
plugins {
|
||||
id("com.android.application")
|
||||
id("kotlin-android")
|
||||
id("kotlin-parcelize")
|
||||
id("kotlin-kapt")
|
||||
id("androidx.navigation.safeargs.kotlin")
|
||||
id("dagger.hilt.android.plugin")
|
||||
id("com.mikepenz.aboutlibraries.plugin")
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = 31
|
||||
buildToolsVersion = "31.0.0"
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "dev.jdtech.jellyfin"
|
||||
minSdk = 24
|
||||
targetSdk = 31
|
||||
versionCode = 4
|
||||
versionName = "0.2.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
getByName("debug") {
|
||||
applicationIdSuffix = ".debug"
|
||||
}
|
||||
create("staging") {
|
||||
initWith(getByName("release"))
|
||||
applicationIdSuffix = ".staging"
|
||||
}
|
||||
getByName("release") {
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = true
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
dataBinding = true
|
||||
viewBinding = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.core:core-ktx:1.6.0")
|
||||
implementation("androidx.core:core-splashscreen:1.0.0-alpha01")
|
||||
implementation("androidx.appcompat:appcompat:1.3.1")
|
||||
|
||||
// Material
|
||||
implementation("com.google.android.material:material:1.4.0")
|
||||
|
||||
// ConstraintLayout
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.0")
|
||||
|
||||
// Navigation
|
||||
val navigationVersion = "2.3.5"
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:$navigationVersion")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:$navigationVersion")
|
||||
|
||||
// RecyclerView
|
||||
implementation("androidx.recyclerview:recyclerview:1.2.1")
|
||||
implementation("androidx.recyclerview:recyclerview-selection:1.1.0")
|
||||
|
||||
// Room
|
||||
val roomVersion = "2.3.0"
|
||||
implementation("androidx.room:room-runtime:$roomVersion")
|
||||
kapt("androidx.room:room-compiler:$roomVersion")
|
||||
implementation("androidx.room:room-ktx:$roomVersion")
|
||||
|
||||
// Preference
|
||||
val preferenceVersion = "1.1.1"
|
||||
implementation("androidx.preference:preference-ktx:$preferenceVersion")
|
||||
|
||||
// Jellyfin
|
||||
val jellyfinVersion = "1.0.3"
|
||||
implementation("org.jellyfin.sdk:jellyfin-platform-android:$jellyfinVersion")
|
||||
|
||||
// Glide
|
||||
val glideVersion = "4.12.0"
|
||||
implementation("com.github.bumptech.glide:glide:$glideVersion")
|
||||
kapt("com.github.bumptech.glide:compiler:$glideVersion")
|
||||
|
||||
// Hilt
|
||||
val hiltVersion = "2.38.1"
|
||||
implementation("com.google.dagger:hilt-android:$hiltVersion")
|
||||
kapt("com.google.dagger:hilt-compiler:$hiltVersion")
|
||||
|
||||
// ExoPlayer
|
||||
val exoplayerVersion = "2.15.0"
|
||||
implementation("com.google.android.exoplayer:exoplayer-core:$exoplayerVersion")
|
||||
implementation("com.google.android.exoplayer:exoplayer-ui:$exoplayerVersion")
|
||||
implementation(files("libs/extension-ffmpeg-release.aar"))
|
||||
|
||||
// MPV
|
||||
implementation(files("libs/libmpv.aar"))
|
||||
|
||||
// Timber
|
||||
val timberVersion = "5.0.1"
|
||||
implementation("com.jakewharton.timber:timber:$timberVersion")
|
||||
|
||||
val aboutLibrariesVersion = "8.9.1"
|
||||
implementation("com.mikepenz:aboutlibraries-core:$aboutLibrariesVersion")
|
||||
implementation("com.mikepenz:aboutlibraries:$aboutLibrariesVersion")
|
||||
|
||||
// Testing
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.3")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
|
||||
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
|
||||
}
|
BIN
app/libs/libmpv.aar
Executable file
BIN
app/libs/libmpv.aar
Executable file
Binary file not shown.
2
app/proguard-rules.pro
vendored
2
app/proguard-rules.pro
vendored
|
@ -1,6 +1,6 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
# proguardFiles setting in build.gradle.kts.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
|
11
app/src/main/assets/mpv.conf
Executable file
11
app/src/main/assets/mpv.conf
Executable file
|
@ -0,0 +1,11 @@
|
|||
### hwdec: try to use hardware decoding
|
||||
# hwdec=mediacodec-copy
|
||||
# hwdec-codecs="h264,hevc,mpeg4,mpeg2video,vp8,vp9,av1"
|
||||
|
||||
### tls: allow self signed certificate
|
||||
# tls-verify=no
|
||||
# tls-ca-file=""
|
||||
|
||||
### sub: scale subtitles with video
|
||||
# sub-scale-with-window=no
|
||||
# sub-use-margins=no
|
BIN
app/src/main/assets/subfont.ttf
Normal file
BIN
app/src/main/assets/subfont.ttf
Normal file
Binary file not shown.
|
@ -1,34 +1,146 @@
|
|||
package dev.jdtech.jellyfin
|
||||
|
||||
import android.os.Build
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.navigation.navArgs
|
||||
import com.google.android.exoplayer2.ui.StyledPlayerView
|
||||
import com.google.android.exoplayer2.C
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer
|
||||
import com.google.android.exoplayer2.trackselection.MappingTrackSelector
|
||||
import com.google.android.exoplayer2.ui.TrackSelectionDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dev.jdtech.jellyfin.databinding.ActivityPlayerBinding
|
||||
import dev.jdtech.jellyfin.dialogs.TrackSelectionDialogFragment
|
||||
import dev.jdtech.jellyfin.mpv.MPVPlayer
|
||||
import dev.jdtech.jellyfin.mpv.TrackType
|
||||
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
|
||||
import timber.log.Timber
|
||||
|
||||
@AndroidEntryPoint
|
||||
class PlayerActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityPlayerBinding
|
||||
private val viewModel: PlayerActivityViewModel by viewModels()
|
||||
|
||||
private val args: PlayerActivityArgs by navArgs()
|
||||
|
||||
private lateinit var playerView: StyledPlayerView
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Timber.d("Creating player activity")
|
||||
setContentView(R.layout.activity_player)
|
||||
binding = ActivityPlayerBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
|
||||
playerView = findViewById(R.id.video_view)
|
||||
binding.playerView.player = viewModel.player
|
||||
|
||||
viewModel.player.observe(this, {
|
||||
playerView.player = it
|
||||
val playerControls = binding.playerView.findViewById<View>(R.id.player_controls)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
binding.playerView.findViewById<View>(R.id.player_controls)
|
||||
.setOnApplyWindowInsetsListener { _, windowInsets ->
|
||||
val cutout = windowInsets.displayCutout
|
||||
playerControls.updatePadding(
|
||||
left = cutout?.safeInsetLeft ?: 0,
|
||||
top = cutout?.safeInsetTop ?: 0,
|
||||
right = cutout?.safeInsetRight ?: 0,
|
||||
bottom = cutout?.safeInsetBottom ?: 0,
|
||||
)
|
||||
return@setOnApplyWindowInsetsListener windowInsets
|
||||
}
|
||||
}
|
||||
|
||||
binding.playerView.findViewById<View>(R.id.back_button).setOnClickListener {
|
||||
onBackPressed()
|
||||
}
|
||||
|
||||
val videoNameTextView = binding.playerView.findViewById<TextView>(R.id.video_name)
|
||||
|
||||
viewModel.currentItemTitle.observe(this, { title ->
|
||||
videoNameTextView.text = title
|
||||
})
|
||||
|
||||
val audioButton = binding.playerView.findViewById<ImageButton>(R.id.btn_audio_track)
|
||||
val subtitleButton = binding.playerView.findViewById<ImageButton>(R.id.btn_subtitle)
|
||||
|
||||
audioButton.isEnabled = false
|
||||
audioButton.imageAlpha = 75
|
||||
|
||||
subtitleButton.isEnabled = false
|
||||
subtitleButton.imageAlpha = 75
|
||||
|
||||
audioButton.setOnClickListener {
|
||||
when (viewModel.player) {
|
||||
is MPVPlayer -> {
|
||||
TrackSelectionDialogFragment(TrackType.AUDIO, viewModel).show(
|
||||
supportFragmentManager,
|
||||
"trackselectiondialog"
|
||||
)
|
||||
}
|
||||
is SimpleExoPlayer -> {
|
||||
val mappedTrackInfo =
|
||||
viewModel.trackSelector.currentMappedTrackInfo ?: return@setOnClickListener
|
||||
|
||||
var audioRenderer: Int? = null
|
||||
for (i in 0 until mappedTrackInfo.rendererCount) {
|
||||
if (isRendererType(mappedTrackInfo, i, C.TRACK_TYPE_AUDIO)) {
|
||||
audioRenderer = i
|
||||
}
|
||||
}
|
||||
|
||||
if (audioRenderer == null) return@setOnClickListener
|
||||
|
||||
val trackSelectionDialogBuilder = TrackSelectionDialogBuilder(
|
||||
this, resources.getString(R.string.select_audio_track),
|
||||
viewModel.trackSelector, audioRenderer
|
||||
)
|
||||
val trackSelectionDialog = trackSelectionDialogBuilder.build()
|
||||
trackSelectionDialog.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subtitleButton.setOnClickListener {
|
||||
when (viewModel.player) {
|
||||
is MPVPlayer -> {
|
||||
TrackSelectionDialogFragment(TrackType.SUBTITLE, viewModel).show(
|
||||
supportFragmentManager,
|
||||
"trackselectiondialog"
|
||||
)
|
||||
}
|
||||
is SimpleExoPlayer -> {
|
||||
val mappedTrackInfo =
|
||||
viewModel.trackSelector.currentMappedTrackInfo ?: return@setOnClickListener
|
||||
|
||||
var subtitleRenderer: Int? = null
|
||||
for (i in 0 until mappedTrackInfo.rendererCount) {
|
||||
if (isRendererType(mappedTrackInfo, i, C.TRACK_TYPE_TEXT)) {
|
||||
subtitleRenderer = i
|
||||
}
|
||||
}
|
||||
|
||||
if (subtitleRenderer == null) return@setOnClickListener
|
||||
|
||||
val trackSelectionDialogBuilder = TrackSelectionDialogBuilder(
|
||||
this, resources.getString(R.string.select_subtile_track),
|
||||
viewModel.trackSelector, subtitleRenderer
|
||||
)
|
||||
val trackSelectionDialog = trackSelectionDialogBuilder.build()
|
||||
trackSelectionDialog.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.fileLoaded.observe(this, {
|
||||
if (it) {
|
||||
audioButton.isEnabled = true
|
||||
audioButton.imageAlpha = 255
|
||||
subtitleButton.isEnabled = true
|
||||
subtitleButton.imageAlpha = 255
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.navigateBack.observe(this, {
|
||||
|
@ -37,21 +149,19 @@ class PlayerActivity : AppCompatActivity() {
|
|||
}
|
||||
})
|
||||
|
||||
if (viewModel.player.value == null) {
|
||||
viewModel.initializePlayer(args.items)
|
||||
}
|
||||
viewModel.initializePlayer(args.items)
|
||||
hideSystemUI()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
viewModel.playWhenReady = viewModel.player.value?.playWhenReady == true
|
||||
playerView.player?.playWhenReady = false
|
||||
viewModel.playWhenReady = viewModel.player.playWhenReady == true
|
||||
viewModel.player.playWhenReady = false
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.player.value?.playWhenReady = viewModel.playWhenReady
|
||||
viewModel.player.playWhenReady = viewModel.playWhenReady
|
||||
hideSystemUI()
|
||||
}
|
||||
|
||||
|
@ -63,6 +173,23 @@ class PlayerActivity : AppCompatActivity() {
|
|||
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
|
||||
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
window.attributes.layoutInDisplayCutoutMode =
|
||||
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
|
||||
}
|
||||
}
|
||||
|
||||
private fun isRendererType(
|
||||
mappedTrackInfo: MappingTrackSelector.MappedTrackInfo,
|
||||
rendererIndex: Int,
|
||||
type: Int
|
||||
): Boolean {
|
||||
val trackGroupArray = mappedTrackInfo.getTrackGroups(rendererIndex)
|
||||
if (trackGroupArray.length == 0) {
|
||||
return false
|
||||
}
|
||||
val trackType = mappedTrackInfo.getRendererType(rendererIndex)
|
||||
return type == trackType
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
package dev.jdtech.jellyfin.dialogs
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import dev.jdtech.jellyfin.mpv.TrackType
|
||||
import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
class TrackSelectionDialogFragment(
|
||||
private val type: String,
|
||||
private val viewModel: PlayerActivityViewModel
|
||||
) : DialogFragment() {
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val trackNames: List<String>
|
||||
when (type) {
|
||||
TrackType.AUDIO -> {
|
||||
trackNames = viewModel.currentAudioTracks.map { track ->
|
||||
if (track.title.isEmpty()) {
|
||||
"${track.lang} - ${track.codec}"
|
||||
} else {
|
||||
"${track.title} - ${track.lang} - ${track.codec}"
|
||||
}
|
||||
}
|
||||
return activity?.let { activity ->
|
||||
val builder = AlertDialog.Builder(activity)
|
||||
builder.setTitle("Select audio track")
|
||||
.setSingleChoiceItems(
|
||||
trackNames.toTypedArray(),
|
||||
viewModel.currentAudioTracks.indexOfFirst { it.selected }) { _, which ->
|
||||
viewModel.switchToTrack(
|
||||
TrackType.AUDIO,
|
||||
viewModel.currentAudioTracks[which]
|
||||
)
|
||||
}
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
TrackType.SUBTITLE -> {
|
||||
trackNames = viewModel.currentSubtitleTracks.map { track ->
|
||||
if (track.title.isEmpty()) {
|
||||
"${track.lang} - ${track.codec}"
|
||||
} else {
|
||||
"${track.title} - ${track.lang} - ${track.codec}"
|
||||
}
|
||||
}
|
||||
return activity?.let { activity ->
|
||||
val builder = AlertDialog.Builder(activity)
|
||||
builder.setTitle("Select subtitle track")
|
||||
.setSingleChoiceItems(
|
||||
trackNames.toTypedArray(),
|
||||
viewModel.currentSubtitleTracks.indexOfFirst { it.selected }) { _, which ->
|
||||
viewModel.switchToTrack(
|
||||
TrackType.SUBTITLE,
|
||||
viewModel.currentSubtitleTracks[which]
|
||||
)
|
||||
}
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
else -> {
|
||||
trackNames = listOf()
|
||||
return activity?.let {
|
||||
val builder = AlertDialog.Builder(it)
|
||||
builder.setTitle("Select ? track")
|
||||
.setMessage("Unknown track type")
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,10 +10,6 @@ import dev.jdtech.jellyfin.R
|
|||
|
||||
class InitializingFragment : Fragment() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
|
|
|
@ -75,7 +75,7 @@ class MediaInfoFragment : Fragment() {
|
|||
} else {
|
||||
binding.originalTitle.visibility = View.GONE
|
||||
}
|
||||
if (item.trailerCount != null && item.trailerCount!! < 1) {
|
||||
if (item.remoteTrailers.isNullOrEmpty()) {
|
||||
binding.trailerButton.visibility = View.GONE
|
||||
}
|
||||
binding.communityRating.visibility = when (item.communityRating != null) {
|
||||
|
@ -147,6 +147,7 @@ class MediaInfoFragment : Fragment() {
|
|||
}
|
||||
|
||||
binding.trailerButton.setOnClickListener {
|
||||
if (viewModel.item.value?.remoteTrailers.isNullOrEmpty()) return@setOnClickListener
|
||||
val intent = Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
Uri.parse(viewModel.item.value?.remoteTrailers?.get(0)?.url)
|
||||
|
|
|
@ -6,6 +6,7 @@ import java.util.*
|
|||
|
||||
@Parcelize
|
||||
data class PlayerItem(
|
||||
val name: String?,
|
||||
val itemId: UUID,
|
||||
val mediaSourceId: String,
|
||||
val playbackPosition: Long
|
||||
|
|
1504
app/src/main/java/dev/jdtech/jellyfin/mpv/MPVPlayer.kt
Normal file
1504
app/src/main/java/dev/jdtech/jellyfin/mpv/MPVPlayer.kt
Normal file
File diff suppressed because it is too large
Load diff
14
app/src/main/java/dev/jdtech/jellyfin/mpv/TrackType.java
Normal file
14
app/src/main/java/dev/jdtech/jellyfin/mpv/TrackType.java
Normal file
|
@ -0,0 +1,14 @@
|
|||
package dev.jdtech.jellyfin.mpv;
|
||||
|
||||
import androidx.annotation.StringDef;
|
||||
|
||||
@StringDef({
|
||||
TrackType.VIDEO,
|
||||
TrackType.AUDIO,
|
||||
TrackType.SUBTITLE,
|
||||
})
|
||||
public @interface TrackType {
|
||||
String VIDEO = "video";
|
||||
String AUDIO = "audio";
|
||||
String SUBTITLE = "sub";
|
||||
}
|
|
@ -86,7 +86,7 @@ constructor(
|
|||
val intros = jellyfinRepository.getIntros(startEpisode.id)
|
||||
for (intro in intros) {
|
||||
if (intro.mediaSources.isNullOrEmpty()) continue
|
||||
playerItems.add(PlayerItem(intro.id, intro.mediaSources?.get(0)?.id!!, 0))
|
||||
playerItems.add(PlayerItem(intro.name, intro.id, intro.mediaSources?.get(0)?.id!!, 0))
|
||||
introsCount += 1
|
||||
}
|
||||
}
|
||||
|
@ -102,6 +102,7 @@ constructor(
|
|||
if (episode.locationType == LocationType.VIRTUAL) continue
|
||||
playerItems.add(
|
||||
PlayerItem(
|
||||
episode.name,
|
||||
episode.id,
|
||||
episode.mediaSources?.get(0)?.id!!,
|
||||
playbackPosition
|
||||
|
|
|
@ -202,7 +202,7 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
|
|||
val intros = jellyfinRepository.getIntros(series.id)
|
||||
for (intro in intros) {
|
||||
if (intro.mediaSources.isNullOrEmpty()) continue
|
||||
playerItems.add(PlayerItem(intro.id, intro.mediaSources?.get(0)?.id!!, 0))
|
||||
playerItems.add(PlayerItem(intro.name, intro.id, intro.mediaSources?.get(0)?.id!!, 0))
|
||||
introsCount += 1
|
||||
}
|
||||
}
|
||||
|
@ -211,6 +211,7 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
|
|||
"Movie" -> {
|
||||
playerItems.add(
|
||||
PlayerItem(
|
||||
series.name,
|
||||
series.id,
|
||||
series.mediaSources?.get(mediaSourceIndex ?: 0)?.id!!,
|
||||
playbackPosition
|
||||
|
@ -231,6 +232,7 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
|
|||
if (episode.locationType == LocationType.VIRTUAL) continue
|
||||
playerItems.add(
|
||||
PlayerItem(
|
||||
episode.name,
|
||||
episode.id,
|
||||
episode.mediaSources?.get(0)?.id!!,
|
||||
0
|
||||
|
@ -250,6 +252,7 @@ constructor(private val jellyfinRepository: JellyfinRepository) : ViewModel() {
|
|||
if (episode.locationType == LocationType.VIRTUAL) continue
|
||||
playerItems.add(
|
||||
PlayerItem(
|
||||
episode.name,
|
||||
episode.id,
|
||||
episode.mediaSources?.get(0)?.id!!,
|
||||
0
|
||||
|
|
|
@ -12,6 +12,8 @@ import com.google.android.exoplayer2.*
|
|||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dev.jdtech.jellyfin.models.PlayerItem
|
||||
import dev.jdtech.jellyfin.mpv.MPVPlayer
|
||||
import dev.jdtech.jellyfin.mpv.TrackType
|
||||
import dev.jdtech.jellyfin.repository.JellyfinRepository
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
@ -23,38 +25,70 @@ import javax.inject.Inject
|
|||
class PlayerActivityViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
private val application: Application,
|
||||
application: Application,
|
||||
private val jellyfinRepository: JellyfinRepository
|
||||
) : ViewModel(), Player.Listener {
|
||||
private var _player = MutableLiveData<SimpleExoPlayer>()
|
||||
var player: LiveData<SimpleExoPlayer> = _player
|
||||
val player: BasePlayer
|
||||
|
||||
private val _navigateBack = MutableLiveData<Boolean>()
|
||||
val navigateBack: LiveData<Boolean> = _navigateBack
|
||||
|
||||
private val _currentItemTitle = MutableLiveData<String>()
|
||||
val currentItemTitle: LiveData<String> = _currentItemTitle
|
||||
|
||||
var currentAudioTracks: MutableList<MPVPlayer.Companion.Track> = mutableListOf()
|
||||
var currentSubtitleTracks: MutableList<MPVPlayer.Companion.Track> = mutableListOf()
|
||||
|
||||
private val _fileLoaded = MutableLiveData(false)
|
||||
val fileLoaded: LiveData<Boolean> = _fileLoaded
|
||||
|
||||
private var items: Array<PlayerItem> = arrayOf()
|
||||
|
||||
val trackSelector = DefaultTrackSelector(application)
|
||||
var playWhenReady = true
|
||||
private var currentWindow = 0
|
||||
private var playbackPosition: Long = 0
|
||||
|
||||
private val sp = PreferenceManager.getDefaultSharedPreferences(application)
|
||||
|
||||
init {
|
||||
val useMpv = sp.getBoolean("mpv_player", false)
|
||||
|
||||
val preferredAudioLanguage = sp.getString("audio_language", null) ?: ""
|
||||
val preferredSubtitleLanguage = sp.getString("subtitle_language", null) ?: ""
|
||||
|
||||
if (useMpv) {
|
||||
val preferredLanguages = mapOf(
|
||||
TrackType.AUDIO to preferredAudioLanguage,
|
||||
TrackType.SUBTITLE to preferredSubtitleLanguage
|
||||
)
|
||||
player = MPVPlayer(
|
||||
application,
|
||||
false,
|
||||
preferredLanguages,
|
||||
sp.getBoolean("mpv_disable_hwdec", false)
|
||||
)
|
||||
} else {
|
||||
val renderersFactory =
|
||||
DefaultRenderersFactory(application).setExtensionRendererMode(
|
||||
DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON
|
||||
)
|
||||
trackSelector.setParameters(
|
||||
trackSelector.buildUponParameters()
|
||||
.setTunnelingEnabled(true)
|
||||
.setPreferredAudioLanguage(preferredAudioLanguage)
|
||||
.setPreferredTextLanguage(preferredSubtitleLanguage)
|
||||
)
|
||||
player = SimpleExoPlayer.Builder(application, renderersFactory)
|
||||
.setTrackSelector(trackSelector)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
fun initializePlayer(
|
||||
items: Array<PlayerItem>
|
||||
) {
|
||||
|
||||
val renderersFactory =
|
||||
DefaultRenderersFactory(application).setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON)
|
||||
val trackSelector = DefaultTrackSelector(application)
|
||||
trackSelector.setParameters(
|
||||
trackSelector.buildUponParameters()
|
||||
.setTunnelingEnabled(true)
|
||||
.setPreferredAudioLanguage(sp.getString("audio_language", null))
|
||||
.setPreferredTextLanguage(sp.getString("subtitle_language", null))
|
||||
)
|
||||
val player = SimpleExoPlayer.Builder(application, renderersFactory)
|
||||
.setTrackSelector(trackSelector)
|
||||
.build()
|
||||
|
||||
this.items = items
|
||||
player.addListener(this)
|
||||
|
||||
viewModelScope.launch {
|
||||
|
@ -76,16 +110,15 @@ constructor(
|
|||
}
|
||||
|
||||
player.setMediaItems(mediaItems, currentWindow, items[0].playbackPosition)
|
||||
player.playWhenReady = playWhenReady
|
||||
player.prepare()
|
||||
_player.value = player
|
||||
player.play()
|
||||
}
|
||||
|
||||
pollPosition(player)
|
||||
}
|
||||
|
||||
private fun releasePlayer() {
|
||||
_player.value?.let { player ->
|
||||
player.let { player ->
|
||||
runBlocking {
|
||||
try {
|
||||
jellyfinRepository.postPlaybackStop(
|
||||
|
@ -98,17 +131,14 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
if (player.value != null) {
|
||||
playWhenReady = player.value!!.playWhenReady
|
||||
playbackPosition = player.value!!.currentPosition
|
||||
currentWindow = player.value!!.currentWindowIndex
|
||||
player.value!!.removeListener(this)
|
||||
player.value!!.release()
|
||||
_player.value = null
|
||||
}
|
||||
playWhenReady = player.playWhenReady
|
||||
playbackPosition = player.currentPosition
|
||||
currentWindow = player.currentWindowIndex
|
||||
player.removeListener(this)
|
||||
player.release()
|
||||
}
|
||||
|
||||
private fun pollPosition(player: SimpleExoPlayer) {
|
||||
private fun pollPosition(player: BasePlayer) {
|
||||
val handler = Handler(Looper.getMainLooper())
|
||||
val runnable = object : Runnable {
|
||||
override fun run() {
|
||||
|
@ -135,6 +165,11 @@ constructor(
|
|||
Timber.d("Playing MediaItem: ${mediaItem?.mediaId}")
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
for (item in items) {
|
||||
if (item.itemId.toString() == player.currentMediaItem?.mediaId ?: "") {
|
||||
_currentItemTitle.value = item.name
|
||||
}
|
||||
}
|
||||
jellyfinRepository.postPlaybackStart(UUID.fromString(mediaItem?.mediaId))
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
|
@ -153,6 +188,23 @@ constructor(
|
|||
}
|
||||
ExoPlayer.STATE_READY -> {
|
||||
stateString = "ExoPlayer.STATE_READY -"
|
||||
currentAudioTracks.clear()
|
||||
currentSubtitleTracks.clear()
|
||||
when (player) {
|
||||
is MPVPlayer -> {
|
||||
player.currentTracks.forEach {
|
||||
when (it.type) {
|
||||
TrackType.AUDIO -> {
|
||||
currentAudioTracks.add(it)
|
||||
}
|
||||
TrackType.SUBTITLE -> {
|
||||
currentSubtitleTracks.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_fileLoaded.value = true
|
||||
}
|
||||
ExoPlayer.STATE_ENDED -> {
|
||||
stateString = "ExoPlayer.STATE_ENDED -"
|
||||
|
@ -167,4 +219,10 @@ constructor(
|
|||
Timber.d("Clearing Player ViewModel")
|
||||
releasePlayer()
|
||||
}
|
||||
|
||||
fun switchToTrack(trackType: String, track: MPVPlayer.Companion.Track) {
|
||||
if (player is MPVPlayer) {
|
||||
player.selectTrack(trackType, isExternal = false, index = track.ffIndex)
|
||||
}
|
||||
}
|
||||
}
|
20
app/src/main/res/drawable/ic_arrow_left.xml
Normal file
20
app/src/main/res/drawable/ic_arrow_left.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M19,12L5,12"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/white"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M12,19l-7,-7l7,-7"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/white"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/ic_closed_caption.xml
Normal file
9
app/src/main/res/drawable/ic_closed_caption.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M19,4L5,4c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,6c0,-1.1 -0.9,-2 -2,-2zM19,18L5,18L5,6h14v12zM7,15h3c0.55,0 1,-0.45 1,-1v-1L9.5,13v0.5h-2v-3h2v0.5L11,11v-1c0,-0.55 -0.45,-1 -1,-1L7,9c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1zM14,15h3c0.55,0 1,-0.45 1,-1v-1h-1.5v0.5h-2v-3h2v0.5L18,11v-1c0,-0.55 -0.45,-1 -1,-1h-3c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1z"
|
||||
android:fillColor="@color/white"/>
|
||||
</vector>
|
20
app/src/main/res/drawable/ic_fast_forward.xml
Normal file
20
app/src/main/res/drawable/ic_fast_forward.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M13,19l9,-7l-9,-7l0,14z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/white"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M2,19l9,-7l-9,-7l0,14z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/white"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
20
app/src/main/res/drawable/ic_pause.xml
Normal file
20
app/src/main/res/drawable/ic_pause.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M6,4h4v16h-4z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@android:color/white"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M14,4h4v16h-4z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@android:color/white"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
20
app/src/main/res/drawable/ic_rewind.xml
Normal file
20
app/src/main/res/drawable/ic_rewind.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M11,19l-9,-7l9,-7l0,14z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/white"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M22,19l-9,-7l9,-7l0,14z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/white"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
20
app/src/main/res/drawable/ic_skip_back.xml
Normal file
20
app/src/main/res/drawable/ic_skip_back.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M19,20l-10,-8l10,-8l0,16z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/white"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M5,19L5,5"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/white"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
20
app/src/main/res/drawable/ic_skip_forward.xml
Normal file
20
app/src/main/res/drawable/ic_skip_forward.xml
Normal file
|
@ -0,0 +1,20 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M5,4l10,8l-10,8l0,-16z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/white"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M19,5L19,19"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/white"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
27
app/src/main/res/drawable/ic_speaker.xml
Normal file
27
app/src/main/res/drawable/ic_speaker.xml
Normal file
|
@ -0,0 +1,27 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M6,2L18,2A2,2 0,0 1,20 4L20,20A2,2 0,0 1,18 22L6,22A2,2 0,0 1,4 20L4,4A2,2 0,0 1,6 2z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/white"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M12,14m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/white"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M12,6L12.01,6"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/white"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
14
app/src/main/res/drawable/transparent_circle_background.xml
Normal file
14
app/src/main/res/drawable/transparent_circle_background.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/white">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="@color/white"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="@android:color/transparent"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
|
@ -1,16 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".PlayerActivity">
|
||||
|
||||
<com.google.android.exoplayer2.ui.StyledPlayerView
|
||||
android:id="@+id/video_view"
|
||||
<com.google.android.exoplayer2.ui.PlayerView
|
||||
android:id="@+id/player_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black"
|
||||
app:show_subtitle_button="true" />
|
||||
app:show_buffering="always" />
|
||||
|
||||
</merge>
|
||||
</FrameLayout>
|
||||
|
|
202
app/src/main/res/layout/exo_player_control_view.xml
Normal file
202
app/src/main/res/layout/exo_player_control_view.xml
Normal file
|
@ -0,0 +1,202 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/player_controls"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/player_background">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintEnd_toStartOf="@id/extra_buttons"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/back_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/transparent_circle_background"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_arrow_left" />
|
||||
|
||||
<Space
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="0dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/video_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Title"
|
||||
android:textColor="@color/white"
|
||||
tools:text="The Dawn of Despair" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/extra_buttons"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btn_audio_track"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:background="@drawable/transparent_circle_background"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_speaker" />
|
||||
|
||||
<Space
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="0dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btn_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:background="@drawable/transparent_circle_background"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_closed_caption" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_prev"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:background="@drawable/transparent_circle_background"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_skip_back" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_rew"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:background="@drawable/transparent_circle_background"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_rewind" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_play"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/circle_background"
|
||||
android:backgroundTint="@color/white"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_play"
|
||||
app:tint="@color/black" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_pause"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/circle_background"
|
||||
android:backgroundTint="@color/white"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_pause"
|
||||
android:visibility="gone"
|
||||
app:tint="@color/black" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_ffwd"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:background="@drawable/transparent_circle_background"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_fast_forward" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/exo_next"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:background="@drawable/transparent_circle_background"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_skip_forward" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/exo_position"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14sp"
|
||||
tools:text="00:00" />
|
||||
|
||||
<Space
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="0dp" />
|
||||
|
||||
<View
|
||||
android:layout_width="4dp"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/white" />
|
||||
|
||||
<Space
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="0dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/exo_duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14sp"
|
||||
tools:text="24:21" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<com.google.android.exoplayer2.ui.DefaultTimeBar
|
||||
android:id="@+id/exo_progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:played_color="@color/primary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
59
app/src/main/res/values-b+es+419/strings.xml
Normal file
59
app/src/main/res/values-b+es+419/strings.xml
Normal file
|
@ -0,0 +1,59 @@
|
|||
<resources>
|
||||
<string name="app_name">Findroid</string>
|
||||
<string name="app_description">Aplicación nativa de terceros para Jellyfin</string>
|
||||
<string name="jellyfin_banner">Emblema Jellyfin</string>
|
||||
<string name="add_server">Agregar servidor</string>
|
||||
<string name="login">Acceso</string>
|
||||
<string name="select_server">Seleccionar servidor</string>
|
||||
<string name="edit_text_server_address_hint">Dirección del servidor</string>
|
||||
<string name="edit_text_username_hint">Usuario</string>
|
||||
<string name="edit_text_password_hint">Contraseña</string>
|
||||
<string name="button_connect">Conectar</string>
|
||||
<string name="button_login">Acceder</string>
|
||||
<string name="server_icon">Icono del servidor</string>
|
||||
<string name="remove_server">Quitar servidor</string>
|
||||
<string name="remove_server_dialog_text">¿Está seguro de quitar el servidor %1$s</string>
|
||||
<string name="remove">Quitar</string>
|
||||
<string name="cancel">Cancelar</string>
|
||||
<string name="title_home">Inicio</string>
|
||||
<string name="title_media">Mis contenidos</string>
|
||||
<string name="title_favorite">Favoritos</string>
|
||||
<string name="title_settings">Configuración</string>
|
||||
<string name="view_all">Ver todo</string>
|
||||
<string name="error_loading_data">Error al cargar datos</string>
|
||||
<string name="retry">Reintentar</string>
|
||||
<string name="genres">Generos</string>
|
||||
<string name="director">Director</string>
|
||||
<string name="writers">Escritores</string>
|
||||
<string name="cast_amp_crew">Reparto y equipo</string>
|
||||
<string name="seasons">Temporadas</string>
|
||||
<string name="play_button_description">Reproducir contenido</string>
|
||||
<string name="trailer_button_description">Ver el trailer</string>
|
||||
<string name="check_button_description">Marcar como visto o No visto</string>
|
||||
<string name="favorite_button_description">Favorito</string>
|
||||
<string name="episode_watched_indicator">Indicador de episodio visto</string>
|
||||
<string name="episode_name">%1$d. %2$s</string>
|
||||
<string name="episode_name_extended">S%1$d:E%2$d - %3$s</string>
|
||||
<string name="next_up">Siguiente</string>
|
||||
<string name="continue_watching">Continuar viendo</string>
|
||||
<string name="series_poster">Poster serie</string>
|
||||
<string name="no_favorites">No tienes favoritos</string>
|
||||
<string name="search">Buscar</string>
|
||||
<string name="no_search_results">Búsqueda sin resultados</string>
|
||||
<string name="settings_category_language">Idioma</string>
|
||||
<string name="settings_preferred_audio_language">Idioma de audio preferido</string>
|
||||
<string name="settings_preferred_subtitle_language">Idioma de subtitulo preferido</string>
|
||||
<string name="initializing">Iniciando…</string>
|
||||
<string name="settings_category_servers">Servidores</string>
|
||||
<string name="manage_servers">Administrar servidores</string>
|
||||
<string name="settings_category_appearance">Apariencia</string>
|
||||
<string name="theme">Tema</string>
|
||||
<string name="error_preparing_player_items">Error preparando elementos.</string>
|
||||
<string name="view_details">Ver detalles</string>
|
||||
<string name="view_details_underlined"><u>Ver detalles</u></string>
|
||||
<string name="about">Acerca</string>
|
||||
<string name="privacy_policy">Política de privacidad</string>
|
||||
<string name="app_info">Información de la App</string>
|
||||
<string name="unknown_error">Error desconocido</string>
|
||||
<string name="latest_library">Últimos %1$s</string>
|
||||
</resources>
|
59
app/src/main/res/values-es-rMX/strings.xml
Normal file
59
app/src/main/res/values-es-rMX/strings.xml
Normal file
|
@ -0,0 +1,59 @@
|
|||
<resources>
|
||||
<string name="app_name">Findroid</string>
|
||||
<string name="app_description">Aplicación nativa de terceros para Jellyfin</string>
|
||||
<string name="jellyfin_banner">Emblema Jellyfin</string>
|
||||
<string name="add_server">Agregar servidor</string>
|
||||
<string name="login">Acceso</string>
|
||||
<string name="select_server">Seleccionar servidor</string>
|
||||
<string name="edit_text_server_address_hint">Dirección del servidor</string>
|
||||
<string name="edit_text_username_hint">Usuario</string>
|
||||
<string name="edit_text_password_hint">Contraseña</string>
|
||||
<string name="button_connect">Conectar</string>
|
||||
<string name="button_login">Acceder</string>
|
||||
<string name="server_icon">Icono del servidor</string>
|
||||
<string name="remove_server">Quitar servidor</string>
|
||||
<string name="remove_server_dialog_text">¿Está seguro de quitar el servidor %1$s</string>
|
||||
<string name="remove">Quitar</string>
|
||||
<string name="cancel">Cancelar</string>
|
||||
<string name="title_home">Inicio</string>
|
||||
<string name="title_media">Mis contenidos</string>
|
||||
<string name="title_favorite">Favoritos</string>
|
||||
<string name="title_settings">Configuración</string>
|
||||
<string name="view_all">Ver todo</string>
|
||||
<string name="error_loading_data">Error al cargar datos</string>
|
||||
<string name="retry">Reintentar</string>
|
||||
<string name="genres">Generos</string>
|
||||
<string name="director">Director</string>
|
||||
<string name="writers">Escritores</string>
|
||||
<string name="cast_amp_crew">Reparto y equipo</string>
|
||||
<string name="seasons">Temporadas</string>
|
||||
<string name="play_button_description">Reproducir contenido</string>
|
||||
<string name="trailer_button_description">Ver el trailer</string>
|
||||
<string name="check_button_description">Marcar cono visto o No visto</string>
|
||||
<string name="favorite_button_description">Favorito</string>
|
||||
<string name="episode_watched_indicator">Indicador de episodio visto</string>
|
||||
<string name="episode_name">%1$d. %2$s</string>
|
||||
<string name="episode_name_extended">S%1$d:E%2$d - %3$s</string>
|
||||
<string name="next_up">Siguiente</string>
|
||||
<string name="continue_watching">Continar viendo</string>
|
||||
<string name="latest_library">Últimos %1$s</string>
|
||||
<string name="series_poster">Poster serie</string>
|
||||
<string name="no_favorites">No tienes favoritos</string>
|
||||
<string name="search">Buscar</string>
|
||||
<string name="no_search_results">Búsqueda sin resultados</string>
|
||||
<string name="settings_category_language">Idioma</string>
|
||||
<string name="settings_preferred_audio_language">Idioma de audio preferido</string>
|
||||
<string name="settings_preferred_subtitle_language">Idioma de subtitulo preferido</string>
|
||||
<string name="initializing">Iniciando…</string>
|
||||
<string name="settings_category_servers">Servidores</string>
|
||||
<string name="manage_servers">Administrar servidores</string>
|
||||
<string name="settings_category_appearance">Apariencia</string>
|
||||
<string name="theme">Tema</string>
|
||||
<string name="error_preparing_player_items">Error preparando elementos.</string>
|
||||
<string name="view_details">Ver detalles</string>
|
||||
<string name="view_details_underlined"><u>Ver detalles</u></string>
|
||||
<string name="about">Acerca</string>
|
||||
<string name="privacy_policy">Política de privacidad</string>
|
||||
<string name="app_info">Información de la App</string>
|
||||
<string name="unknown_error">Error desconocido</string>
|
||||
</resources>
|
59
app/src/main/res/values-es/strings.xml
Normal file
59
app/src/main/res/values-es/strings.xml
Normal file
|
@ -0,0 +1,59 @@
|
|||
<resources>
|
||||
<string name="app_name">Findroid</string>
|
||||
<string name="app_description">Aplicación nativa de terceros para Jellyfin</string>
|
||||
<string name="jellyfin_banner">Emblema Jellyfin</string>
|
||||
<string name="add_server">Agregar servidor</string>
|
||||
<string name="login">Acceso</string>
|
||||
<string name="select_server">Seleccionar servidor</string>
|
||||
<string name="edit_text_server_address_hint">Dirección del servidor</string>
|
||||
<string name="edit_text_username_hint">Usuario</string>
|
||||
<string name="edit_text_password_hint">Contraseña</string>
|
||||
<string name="button_connect">Conectar</string>
|
||||
<string name="button_login">Acceder</string>
|
||||
<string name="server_icon">Icono del servidor</string>
|
||||
<string name="remove_server">Quitar servidor</string>
|
||||
<string name="remove_server_dialog_text">¿Está seguro de quitar el servidor %1$s</string>
|
||||
<string name="remove">Quitar</string>
|
||||
<string name="cancel">Cancelar</string>
|
||||
<string name="title_home">Inicio</string>
|
||||
<string name="title_media">Mis medios</string>
|
||||
<string name="title_favorite">Favoritos</string>
|
||||
<string name="title_settings">Ajustes</string>
|
||||
<string name="view_all">Ver todo</string>
|
||||
<string name="error_loading_data">Error al cargar datos</string>
|
||||
<string name="retry">Reintentar</string>
|
||||
<string name="genres">Géneros</string>
|
||||
<string name="director">Director</string>
|
||||
<string name="writers">Escritores</string>
|
||||
<string name="cast_amp_crew">Reparto y equipo</string>
|
||||
<string name="seasons">Temporadas</string>
|
||||
<string name="play_button_description">Reproducir contenido</string>
|
||||
<string name="trailer_button_description">Ver el adelanto</string>
|
||||
<string name="check_button_description">Marcar como visto o No visto</string>
|
||||
<string name="favorite_button_description">Favorito</string>
|
||||
<string name="episode_watched_indicator">Indicador de episodio visto</string>
|
||||
<string name="episode_name">%1$d. %2$s</string>
|
||||
<string name="episode_name_extended">S%1$d:E%2$d - %3$s</string>
|
||||
<string name="next_up">Siguiente</string>
|
||||
<string name="continue_watching">Continar viendo</string>
|
||||
<string name="latest_library">Últimos %1$s</string>
|
||||
<string name="series_poster">Poster serie</string>
|
||||
<string name="no_favorites">No tienes favoritos</string>
|
||||
<string name="search">Buscar</string>
|
||||
<string name="no_search_results">Búsqueda sin resultados</string>
|
||||
<string name="settings_category_language">Idioma</string>
|
||||
<string name="settings_preferred_audio_language">Idioma de audio preferido</string>
|
||||
<string name="settings_preferred_subtitle_language">Idioma de subtitulo preferido</string>
|
||||
<string name="initializing">Iniciando…</string>
|
||||
<string name="settings_category_servers">Servidores</string>
|
||||
<string name="manage_servers">Administrar servidores</string>
|
||||
<string name="settings_category_appearance">Apariencia</string>
|
||||
<string name="theme">Tema</string>
|
||||
<string name="error_preparing_player_items">Error preparando elementos.</string>
|
||||
<string name="view_details">Ver detalles</string>
|
||||
<string name="view_details_underlined"><u>Ver detalles</u></string>
|
||||
<string name="about">Acerca</string>
|
||||
<string name="privacy_policy">Política de privacidad</string>
|
||||
<string name="app_info">Información de aplicación</string>
|
||||
<string name="unknown_error">Error desconocido</string>
|
||||
</resources>
|
|
@ -15,4 +15,5 @@
|
|||
<color name="white">#FFFFFFFF</color>
|
||||
<color name="red">#EB5757</color>
|
||||
<color name="yellow">#F2C94C</color>
|
||||
<color name="player_background">#AA000000</color>
|
||||
</resources>
|
5
app/src/main/res/values/drawables.xml
Normal file
5
app/src/main/res/values/drawables.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<drawable name="exo_styled_controls_play">@drawable/ic_play</drawable>
|
||||
<drawable name="exo_styled_controls_pause">@drawable/ic_pause</drawable>
|
||||
</resources>
|
|
@ -190,189 +190,189 @@
|
|||
|
||||
<string-array name="languages_values">
|
||||
<item>null</item>
|
||||
<item>ab</item>
|
||||
<item>aa</item>
|
||||
<item>af</item>
|
||||
<item>ak</item>
|
||||
<item>sq</item>
|
||||
<item>am</item>
|
||||
<item>ar</item>
|
||||
<item>an</item>
|
||||
<item>hy</item>
|
||||
<item>as</item>
|
||||
<item>av</item>
|
||||
<item>ae</item>
|
||||
<item>ay</item>
|
||||
<item>az</item>
|
||||
<item>bm</item>
|
||||
<item>ba</item>
|
||||
<item>eu</item>
|
||||
<item>be</item>
|
||||
<item>bn</item>
|
||||
<item>bh</item>
|
||||
<item>bi</item>
|
||||
<item>nb</item>
|
||||
<item>bs</item>
|
||||
<item>br</item>
|
||||
<item>bg</item>
|
||||
<item>my</item>
|
||||
<item>ca</item>
|
||||
<item>km</item>
|
||||
<item>ch</item>
|
||||
<item>ce</item>
|
||||
<item>ny</item>
|
||||
<item>zh</item>
|
||||
<item>cu</item>
|
||||
<item>cv</item>
|
||||
<item>kw</item>
|
||||
<item>co</item>
|
||||
<item>cr</item>
|
||||
<item>hr</item>
|
||||
<item>cs</item>
|
||||
<item>da</item>
|
||||
<item>dv</item>
|
||||
<item>nl</item>
|
||||
<item>dz</item>
|
||||
<item>en</item>
|
||||
<item>eo</item>
|
||||
<item>et</item>
|
||||
<item>ee</item>
|
||||
<item>fo</item>
|
||||
<item>fj</item>
|
||||
<item>fi</item>
|
||||
<item>fr</item>
|
||||
<item>ff</item>
|
||||
<item>gd</item>
|
||||
<item>gl</item>
|
||||
<item>lg</item>
|
||||
<item>ka</item>
|
||||
<item>de</item>
|
||||
<item>el</item>
|
||||
<item>gn</item>
|
||||
<item>gu</item>
|
||||
<item>ht</item>
|
||||
<item>ha</item>
|
||||
<item>he</item>
|
||||
<item>hz</item>
|
||||
<item>hi</item>
|
||||
<item>ho</item>
|
||||
<item>hu</item>
|
||||
<item>is</item>
|
||||
<item>io</item>
|
||||
<item>ig</item>
|
||||
<item>id</item>
|
||||
<item>ia</item>
|
||||
<item>ie</item>
|
||||
<item>iu</item>
|
||||
<item>ik</item>
|
||||
<item>ga</item>
|
||||
<item>it</item>
|
||||
<item>ja</item>
|
||||
<item>jv</item>
|
||||
<item>kl</item>
|
||||
<item>kn</item>
|
||||
<item>kr</item>
|
||||
<item>ks</item>
|
||||
<item>kk</item>
|
||||
<item>ki</item>
|
||||
<item>rw</item>
|
||||
<item>ky</item>
|
||||
<item>kv</item>
|
||||
<item>kg</item>
|
||||
<item>ko</item>
|
||||
<item>kj</item>
|
||||
<item>ku</item>
|
||||
<item>lo</item>
|
||||
<item>la</item>
|
||||
<item>lv</item>
|
||||
<item>li</item>
|
||||
<item>ln</item>
|
||||
<item>lt</item>
|
||||
<item>lu</item>
|
||||
<item>lb</item>
|
||||
<item>mk</item>
|
||||
<item>mg</item>
|
||||
<item>ms</item>
|
||||
<item>ml</item>
|
||||
<item>mt</item>
|
||||
<item>gv</item>
|
||||
<item>mi</item>
|
||||
<item>mr</item>
|
||||
<item>mh</item>
|
||||
<item>mn</item>
|
||||
<item>na</item>
|
||||
<item>nv</item>
|
||||
<item>nd</item>
|
||||
<item>nr</item>
|
||||
<item>ng</item>
|
||||
<item>ne</item>
|
||||
<item>se</item>
|
||||
<item>no</item>
|
||||
<item>nn</item>
|
||||
<item>oc</item>
|
||||
<item>oj</item>
|
||||
<item>or</item>
|
||||
<item>om</item>
|
||||
<item>os</item>
|
||||
<item>pi</item>
|
||||
<item>pa</item>
|
||||
<item>fa</item>
|
||||
<item>pl</item>
|
||||
<item>pt</item>
|
||||
<item>ps</item>
|
||||
<item>qu</item>
|
||||
<item>ro</item>
|
||||
<item>rm</item>
|
||||
<item>rn</item>
|
||||
<item>ru</item>
|
||||
<item>sm</item>
|
||||
<item>sg</item>
|
||||
<item>sa</item>
|
||||
<item>sc</item>
|
||||
<item>sr</item>
|
||||
<item>sn</item>
|
||||
<item>ii</item>
|
||||
<item>sd</item>
|
||||
<item>si</item>
|
||||
<item>sk</item>
|
||||
<item>sl</item>
|
||||
<item>so</item>
|
||||
<item>st</item>
|
||||
<item>es</item>
|
||||
<item>su</item>
|
||||
<item>sw</item>
|
||||
<item>ss</item>
|
||||
<item>sv</item>
|
||||
<item>tl</item>
|
||||
<item>ty</item>
|
||||
<item>tg</item>
|
||||
<item>ta</item>
|
||||
<item>tt</item>
|
||||
<item>te</item>
|
||||
<item>th</item>
|
||||
<item>bo</item>
|
||||
<item>ti</item>
|
||||
<item>to</item>
|
||||
<item>ts</item>
|
||||
<item>tn</item>
|
||||
<item>tr</item>
|
||||
<item>tk</item>
|
||||
<item>tw</item>
|
||||
<item>ug</item>
|
||||
<item>uk</item>
|
||||
<item>ur</item>
|
||||
<item>uz</item>
|
||||
<item>ve</item>
|
||||
<item>vi</item>
|
||||
<item>vo</item>
|
||||
<item>wa</item>
|
||||
<item>cy</item>
|
||||
<item>fy</item>
|
||||
<item>wo</item>
|
||||
<item>xh</item>
|
||||
<item>yi</item>
|
||||
<item>yo</item>
|
||||
<item>za</item>
|
||||
<item>zu</item>
|
||||
<item>abk</item>
|
||||
<item>aar</item>
|
||||
<item>afr</item>
|
||||
<item>aka</item>
|
||||
<item>sqi</item>
|
||||
<item>amh</item>
|
||||
<item>ara</item>
|
||||
<item>arg</item>
|
||||
<item>hye</item>
|
||||
<item>asm</item>
|
||||
<item>ava</item>
|
||||
<item>ave</item>
|
||||
<item>aym</item>
|
||||
<item>aze</item>
|
||||
<item>bam</item>
|
||||
<item>bak</item>
|
||||
<item>eus</item>
|
||||
<item>bel</item>
|
||||
<item>ben</item>
|
||||
<item>bih</item>
|
||||
<item>bis</item>
|
||||
<item>nob</item>
|
||||
<item>bos</item>
|
||||
<item>bre</item>
|
||||
<item>bul</item>
|
||||
<item>bur</item>
|
||||
<item>cat</item>
|
||||
<item>khm</item>
|
||||
<item>cha</item>
|
||||
<item>che</item>
|
||||
<item>nya</item>
|
||||
<item>chi</item>
|
||||
<item>chu</item>
|
||||
<item>chv</item>
|
||||
<item>cor</item>
|
||||
<item>cos</item>
|
||||
<item>cre</item>
|
||||
<item>hrv</item>
|
||||
<item>cze</item>
|
||||
<item>dan</item>
|
||||
<item>div</item>
|
||||
<item>dut</item>
|
||||
<item>dzo</item>
|
||||
<item>eng</item>
|
||||
<item>epo</item>
|
||||
<item>est</item>
|
||||
<item>ewe</item>
|
||||
<item>fao</item>
|
||||
<item>fij</item>
|
||||
<item>fin</item>
|
||||
<item>fre</item>
|
||||
<item>ful</item>
|
||||
<item>gla</item>
|
||||
<item>glg</item>
|
||||
<item>lug</item>
|
||||
<item>geo</item>
|
||||
<item>ger</item>
|
||||
<item>gre</item>
|
||||
<item>grn</item>
|
||||
<item>guj</item>
|
||||
<item>hat</item>
|
||||
<item>hau</item>
|
||||
<item>heb</item>
|
||||
<item>her</item>
|
||||
<item>hin</item>
|
||||
<item>hmo</item>
|
||||
<item>hun</item>
|
||||
<item>ice</item>
|
||||
<item>ido</item>
|
||||
<item>ibo</item>
|
||||
<item>ind</item>
|
||||
<item>ina</item>
|
||||
<item>ile</item>
|
||||
<item>iku</item>
|
||||
<item>ipk</item>
|
||||
<item>gle</item>
|
||||
<item>ita</item>
|
||||
<item>jpn</item>
|
||||
<item>jav</item>
|
||||
<item>kal</item>
|
||||
<item>kan</item>
|
||||
<item>kau</item>
|
||||
<item>kas</item>
|
||||
<item>kaz</item>
|
||||
<item>kik</item>
|
||||
<item>kin</item>
|
||||
<item>kir</item>
|
||||
<item>kom</item>
|
||||
<item>kon</item>
|
||||
<item>kor</item>
|
||||
<item>kua</item>
|
||||
<item>kur</item>
|
||||
<item>lao</item>
|
||||
<item>lat</item>
|
||||
<item>lav</item>
|
||||
<item>lim</item>
|
||||
<item>lin</item>
|
||||
<item>lit</item>
|
||||
<item>lub</item>
|
||||
<item>ltz</item>
|
||||
<item>mac</item>
|
||||
<item>mlg</item>
|
||||
<item>may</item>
|
||||
<item>mal</item>
|
||||
<item>mlt</item>
|
||||
<item>glv</item>
|
||||
<item>mao</item>
|
||||
<item>mar</item>
|
||||
<item>mah</item>
|
||||
<item>mon</item>
|
||||
<item>nau</item>
|
||||
<item>nav</item>
|
||||
<item>nde</item>
|
||||
<item>nbl</item>
|
||||
<item>ndo</item>
|
||||
<item>nep</item>
|
||||
<item>sme</item>
|
||||
<item>nor</item>
|
||||
<item>nno</item>
|
||||
<item>oci</item>
|
||||
<item>oji</item>
|
||||
<item>ori</item>
|
||||
<item>orm</item>
|
||||
<item>oss</item>
|
||||
<item>pli</item>
|
||||
<item>pan</item>
|
||||
<item>per</item>
|
||||
<item>pol</item>
|
||||
<item>por</item>
|
||||
<item>pus</item>
|
||||
<item>que</item>
|
||||
<item>rum</item>
|
||||
<item>roh</item>
|
||||
<item>run</item>
|
||||
<item>rus</item>
|
||||
<item>smo</item>
|
||||
<item>sag</item>
|
||||
<item>san</item>
|
||||
<item>srd</item>
|
||||
<item>srp</item>
|
||||
<item>sna</item>
|
||||
<item>iii</item>
|
||||
<item>snd</item>
|
||||
<item>sin</item>
|
||||
<item>slo</item>
|
||||
<item>slv</item>
|
||||
<item>som</item>
|
||||
<item>sot</item>
|
||||
<item>spa</item>
|
||||
<item>sun</item>
|
||||
<item>swa</item>
|
||||
<item>ssw</item>
|
||||
<item>swe</item>
|
||||
<item>tgl</item>
|
||||
<item>tah</item>
|
||||
<item>tgk</item>
|
||||
<item>tam</item>
|
||||
<item>tat</item>
|
||||
<item>tel</item>
|
||||
<item>tha</item>
|
||||
<item>tib</item>
|
||||
<item>tir</item>
|
||||
<item>ton</item>
|
||||
<item>tso</item>
|
||||
<item>tsn</item>
|
||||
<item>tur</item>
|
||||
<item>tuk</item>
|
||||
<item>twi</item>
|
||||
<item>uig</item>
|
||||
<item>ukr</item>
|
||||
<item>urd</item>
|
||||
<item>uzb</item>
|
||||
<item>ven</item>
|
||||
<item>vie</item>
|
||||
<item>vol</item>
|
||||
<item>wln</item>
|
||||
<item>wel</item>
|
||||
<item>fry</item>
|
||||
<item>wol</item>
|
||||
<item>xho</item>
|
||||
<item>yid</item>
|
||||
<item>yor</item>
|
||||
<item>zha</item>
|
||||
<item>zul</item>
|
||||
</string-array>
|
||||
</resources>
|
|
@ -56,4 +56,10 @@
|
|||
<string name="privacy_policy">Privacy policy</string>
|
||||
<string name="app_info">App info</string>
|
||||
<string name="unknown_error">Unknown error</string>
|
||||
<string name="select_audio_track">Select audio track</string>
|
||||
<string name="select_subtile_track">Select subtitle track</string>
|
||||
<string name="mpv_player">MPV Player</string>
|
||||
<string name="mpv_player_summary">Use the experimental MPV Player to play videos. MPV has support for more video, audio and subtitle codecs.</string>
|
||||
<string name="force_software_decoding">Force software decoding</string>
|
||||
<string name="force_software_decoding_summary">Disable hardware decoding and use software decoding. Can be useful if hardware decoding gives weird artifacts.</string>
|
||||
</resources>
|
|
@ -39,6 +39,18 @@
|
|||
app:useSimpleSummaryProvider="true" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="Player">
|
||||
<SwitchPreference
|
||||
app:key="mpv_player"
|
||||
app:title="@string/mpv_player"
|
||||
app:summary="@string/mpv_player_summary"/>
|
||||
<SwitchPreference
|
||||
app:key="mpv_disable_hwdec"
|
||||
app:dependency="mpv_player"
|
||||
app:title="@string/force_software_decoding"
|
||||
app:summary="@string/force_software_decoding_summary"/>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/about">
|
||||
|
||||
<Preference
|
||||
|
|
37
build.gradle
37
build.gradle
|
@ -1,37 +0,0 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
ext.kotlin_version = "1.5.30"
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url "https://plugins.gradle.org/m2/"
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.0.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
def nav_version = "2.3.5"
|
||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
|
||||
|
||||
def hilt_version = "2.38.1"
|
||||
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
|
||||
|
||||
def about_libraries_version = "8.9.1"
|
||||
classpath "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$about_libraries_version"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
37
build.gradle.kts
Normal file
37
build.gradle.kts
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
val kotlinVersion = "1.5.30"
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven {
|
||||
url = uri("https://plugins.gradle.org/m2/")
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:7.0.2")
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle.kts files
|
||||
val navVersion = "2.3.5"
|
||||
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$navVersion")
|
||||
|
||||
val hiltVersion = "2.38.1"
|
||||
classpath("com.google.dagger:hilt-android-gradle-plugin:$hiltVersion")
|
||||
|
||||
val aboutLibrariesVersion = "8.9.1"
|
||||
classpath("com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:$aboutLibrariesVersion")
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
tasks.create<Delete>("clean") {
|
||||
delete(rootProject.buildDir)
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg version="1.1" viewBox="0 0 1536 512" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.text {
|
||||
fill: #000;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.text {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<defs>
|
||||
<linearGradient id="linear-gradient-6" x1="110.25" x2="496.14" y1="213.3" y2="436.09" gradientTransform="matrix(.6218 0 0 .6218 100.51 103.53)" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3ddc84" offset="0"/>
|
||||
<stop stop-color="#00A4DC" offset="1"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<title>banner-dark</title>
|
||||
<g id="banner-dark">
|
||||
<path id="logo" d="m307.94 275.03 15.785-27.34c2.192-3.7916-3.4954-7.0797-5.6875-3.288l-15.985 27.686c-12.223-5.5788-25.952-8.6852-40.6-8.6852-14.648 0-28.375 3.1109-40.598 8.6852l-15.983-27.686c-2.1884-3.7952-7.8773-0.51167-5.6889 3.2836l15.787 27.345c-27.107 14.743-45.647 42.185-48.359 74.607h189.69c-2.715-32.422-21.254-59.863-48.359-74.607m-46.458-252.33c-61.813 0-260.7 360.64-230.39 421.55 30.305 60.91 430.79 60.208 460.79 0 30.004-60.208-168.58-421.55-230.39-421.55zm151.02 368.77c-19.668 39.436-282.07 39.938-301.94 0-19.869-39.938 110.48-276.25 150.92-276.25s170.69 236.72 151.02 276.25z" fill="url(#linear-gradient-6)" stroke-width=".14885"/>
|
||||
<g class="text" style="font-variation-settings:'wght' 400" aria-label="Findroid">
|
||||
<path d="m569.69 357.11q-3.7333 0-6.1333-2.4-2.1333-2.4-2.1333-5.6v-170.67q0-3.2 2.4-5.6t5.6-2.4h97.067q3.4667 0 5.6 2.4 2.4 2.1333 2.4 5.6 0 3.2-2.4 5.6-2.1333 2.1333-5.6 2.1333h-89.867l1.0667-1.6v70.933l-1.3333-2.4h78.133q3.4667 0 5.6 2.4 2.4 2.1333 2.4 5.3333 0 3.4667-2.4 5.6-2.1333 2.1333-5.6 2.1333h-78.667l1.8667-2.1333v82.667q0 3.2-2.4 5.6-2.1333 2.4-5.6 2.4z"/>
|
||||
<path d="m723.83 349.11q0 3.2-2.4 5.6t-5.6 2.4q-3.4667 0-5.8667-2.4-2.1333-2.4-2.1333-5.6v-122.67q0-3.2 2.1333-5.6 2.4-2.4 5.8667-2.4t5.6 2.4q2.4 2.4 2.4 5.6zm-8-148.53q-5.6 0-8.5333-2.4-2.6667-2.6667-2.6667-7.4667v-2.6667q0-4.8 2.9333-7.2 3.2-2.6667 8.5333-2.6667 5.0667 0 7.7333 2.6667 2.9333 2.4 2.9333 7.2v2.6667q0 4.8-2.9333 7.4667-2.6667 2.4-8 2.4z"/>
|
||||
<path d="m829.69 217.65q17.6 0 28 7.2 10.667 6.9333 15.2 19.2 4.8 12 4.8 26.667v78.4q0 3.2-2.4 5.6t-5.6 2.4q-3.7333 0-5.8667-2.4t-2.1333-5.6v-77.6q0-10.667-3.4667-19.467t-11.467-14.133q-7.7333-5.3333-20.533-5.3333-11.467 0-21.867 5.3333-10.133 5.3333-16.533 14.133-6.4 8.8-6.4 19.467v77.6q0 3.2-2.4 5.6-2.4 2.4-5.6 2.4-3.7333 0-5.8667-2.4-2.1333-2.4-2.1333-5.6v-119.47q0-3.2 2.1333-5.6 2.4-2.4 5.8667-2.4t5.6 2.4q2.4 2.4 2.4 5.6v22.4l-6.1333 9.6q0.53333-8.5333 5.3333-16.267 5.0667-8 12.8-14.133 7.7334-6.4 17.067-9.8667 9.6-3.7333 19.2-3.7333z"/>
|
||||
<path d="m1030.2 159.78q3.4667 0 5.6 2.4 2.4 2.1333 2.4 5.6v181.33q0 3.2-2.4 5.6t-5.6 2.4q-3.7333 0-5.8666-2.4-2.1334-2.4-2.1334-5.6v-31.733l4.5334-3.7333q0 7.4667-4 15.733-4 8-11.467 14.933-7.2 6.9333-17.067 11.2-9.6 4.2667-21.067 4.2667-17.6 0-32-9.3333-14.133-9.3334-22.4-25.333-8.2667-16-8.2667-36.533 0-20.267 8.2667-36.267 8.2667-16.267 22.4-25.333 14.133-9.3333 31.733-9.3333 11.2 0 21.067 4 9.8667 4 17.333 10.933 7.7333 6.9333 12 16 4.5333 8.8 4.5333 18.4l-5.6-4v-95.2q0-3.2 2.1334-5.6 2.1333-2.4 5.8666-2.4zm-55.467 185.07q14.133 0 25.067-7.2 10.933-7.4667 17.067-20 6.4-12.8 6.4-29.067 0-16-6.4-28.533-6.1334-12.8-17.067-20-10.933-7.4667-25.067-7.4667-13.867 0-25.067 7.4667-10.933 7.2-17.333 20-6.1334 12.533-6.1334 28.533t6.1334 28.8q6.4 12.8 17.333 20.267 11.2 7.2 25.067 7.2z"/>
|
||||
<path d="m1088.1 357.11q-3.7333 0-5.8667-2.4-2.1333-2.4-2.1333-5.6v-119.47q0-3.2 2.1333-5.6 2.4-2.4 5.8667-2.4t5.6 2.4q2.4 2.4 2.4 5.6v40l-4 0.8q0.8-9.3333 4.5333-18.4 4-9.3334 10.667-17.067t15.733-12.533q9.3333-4.8 20.8-4.8 4.8 0 9.3333 2.1333 4.5334 1.8667 4.5334 6.4 0 4-2.1334 6.1333-2.1333 2.1333-5.0666 2.1333-2.4 0-5.3334-1.3333-2.6666-1.3333-7.2-1.3333-7.4667 0-14.933 4.5333-7.4667 4.2667-13.6 11.733-6.1334 7.4667-9.8667 16.8-3.4667 9.0667-3.4667 18.4v65.867q0 3.2-2.4 5.6t-5.6 2.4z"/>
|
||||
<path d="m1302.8 288.85q0 20.267-9.0667 36.533-8.8 16-24 25.333-15.2 9.0667-34.4 9.0667-18.933 0-34.4-9.0667-15.2-9.3334-24.267-25.333-8.8-16.267-8.8-36.533 0-20.533 8.8-36.533 9.0667-16 24.267-25.333 15.467-9.3333 34.4-9.3333 19.2 0 34.4 9.3333 15.2 9.3334 24 25.333 9.0667 16 9.0667 36.533zm-16 0q0-16.267-6.6667-28.8-6.6666-12.8-18.4-20-11.467-7.4667-26.4-7.4667-14.667 0-26.4 7.4667-11.467 7.2-18.4 20-6.6667 12.533-6.6667 28.8 0 16.267 6.6667 28.8 6.9334 12.533 18.4 20 11.733 7.2 26.4 7.2 14.933 0 26.4-7.2 11.733-7.4667 18.4-20 6.6667-12.533 6.6667-28.8z"/>
|
||||
<path d="m1351 349.11q0 3.2-2.4 5.6t-5.6 2.4q-3.4667 0-5.8667-2.4-2.1333-2.4-2.1333-5.6v-122.67q0-3.2 2.1333-5.6 2.4-2.4 5.8667-2.4 3.4666 0 5.6 2.4 2.4 2.4 2.4 5.6zm-8-148.53q-5.6 0-8.5334-2.4-2.6666-2.6667-2.6666-7.4667v-2.6667q0-4.8 2.9333-7.2 3.2-2.6667 8.5333-2.6667 5.0667 0 7.7334 2.6667 2.9333 2.4 2.9333 7.2v2.6667q0 4.8-2.9333 7.4667-2.6667 2.4-8 2.4z"/>
|
||||
<path d="m1503.3 159.78q3.4667 0 5.6 2.4 2.4 2.1333 2.4 5.6v181.33q0 3.2-2.4 5.6t-5.6 2.4q-3.7333 0-5.8667-2.4-2.1333-2.4-2.1333-5.6v-31.733l4.5333-3.7333q0 7.4667-4 15.733-4 8-11.467 14.933-7.2 6.9333-17.067 11.2-9.6001 4.2667-21.067 4.2667-17.6 0-32-9.3333-14.133-9.3334-22.4-25.333-8.2667-16-8.2667-36.533 0-20.267 8.2667-36.267 8.2667-16.267 22.4-25.333 14.133-9.3333 31.733-9.3333 11.2 0 21.067 4 9.8667 4 17.333 10.933 7.7334 6.9333 12 16 4.5333 8.8 4.5333 18.4l-5.6-4v-95.2q0-3.2 2.1333-5.6 2.1334-2.4 5.8667-2.4zm-55.467 185.07q14.133 0 25.067-7.2 10.933-7.4667 17.067-20 6.4001-12.8 6.4001-29.067 0-16-6.4001-28.533-6.1333-12.8-17.067-20-10.933-7.4667-25.067-7.4667-13.867 0-25.067 7.4667-10.933 7.2-17.333 20-6.1333 12.533-6.1333 28.533t6.1333 28.8q6.4 12.8 17.333 20.267 11.2 7.2 25.067 7.2z"/>
|
||||
</g>
|
||||
<g class="text" style="font-variation-settings:'wght' 400" aria-label="For Jellyfin">
|
||||
<path d="m1093.4 453.74q-1.1947 0-1.9627-0.768-0.6826-0.768-0.6826-1.792v-54.613q0-1.024 0.768-1.792t1.792-0.768h31.061q1.1093 0 1.792 0.768 0.768 0.68267 0.768 1.792 0 1.024-0.768 1.792-0.6827 0.68266-1.792 0.68266h-28.757l0.3413-0.512v22.699l-0.4267-0.768h25.003q1.1093 0 1.792 0.768 0.768 0.68267 0.768 1.7067 0 1.1093-0.768 1.792-0.6827 0.68266-1.792 0.68266h-25.173l0.5973-0.68266v26.453q0 1.024-0.768 1.792-0.6827 0.768-1.792 0.768z"/>
|
||||
<path d="m1176.8 431.9q0 6.4853-2.9013 11.691-2.816 5.12-7.68 8.1067-4.864 2.9013-11.008 2.9013-6.0587 0-11.008-2.9013-4.864-2.9867-7.7653-8.1067-2.816-5.2053-2.816-11.691 0-6.5707 2.816-11.691 2.9013-5.12 7.7653-8.1067 4.9493-2.9867 11.008-2.9867 6.144 0 11.008 2.9867 4.864 2.9867 7.68 8.1067 2.9013 5.12 2.9013 11.691zm-5.12 0q0-5.2053-2.1333-9.216-2.1333-4.096-5.888-6.4-3.6693-2.3893-8.448-2.3893-4.6933 0-8.448 2.3893-3.6693 2.304-5.888 6.4-2.1333 4.0107-2.1333 9.216 0 5.2053 2.1333 9.216 2.2187 4.0107 5.888 6.4 3.7547 2.304 8.448 2.304 4.7787 0 8.448-2.304 3.7547-2.3893 5.888-6.4 2.1333-4.0107 2.1333-9.216z"/>
|
||||
<path d="m1189.9 453.74q-1.1947 0-1.8774-0.768-0.6826-0.768-0.6826-1.792v-38.229q0-1.024 0.6826-1.792 0.768-0.768 1.8774-0.768 1.1093 0 1.792 0.768 0.768 0.768 0.768 1.792v12.8l-1.28 0.256q0.256-2.9867 1.4506-5.888 1.28-2.9867 3.4134-5.4613 2.1333-2.4747 5.0346-4.0107 2.9867-1.536 6.656-1.536 1.536 0 2.9867 0.68267 1.4507 0.59733 1.4507 2.048 0 1.28-0.6827 1.9627-0.6827 0.68267-1.6213 0.68267-0.768 0-1.7067-0.42667-0.8533-0.42666-2.304-0.42666-2.3893 0-4.7787 1.4507-2.3893 1.3653-4.352 3.7547-1.9626 2.3893-3.1573 5.376-1.1093 2.9013-1.1093 5.888v21.077q0 1.024-0.768 1.792t-1.792 0.768z"/>
|
||||
<path d="m1258.9 454.59q-5.376 0-9.6427-2.9867-4.2667-2.9867-6.4853-7.8507-0.4267-0.768-0.4267-1.3653 0-1.1093 0.8533-1.7067 0.8534-0.68266 1.7067-0.68266t1.3653 0.42666q0.5974 0.42667 1.024 1.024 1.6214 3.584 4.6934 5.8027 3.072 2.2187 6.912 2.2187 3.9253 0 6.912-1.6213 2.9866-1.7067 4.608-4.6933 1.7066-2.9867 1.7066-6.8267v-39.765q0-1.024 0.768-1.792 0.8534-0.768 1.9627-0.768 1.1947 0 1.8773 0.768 0.768 0.768 0.768 1.792v39.765q0 5.2907-2.3893 9.472-2.3893 4.096-6.5707 6.4853-4.1813 2.304-9.6426 2.304z"/>
|
||||
<path d="m1311.6 454.59q-6.5706 0-11.605-2.816t-7.8507-7.8507q-2.816-5.0347-2.816-11.776 0-7.2533 2.816-12.373 2.9014-5.12 7.424-7.8507 4.608-2.816 9.728-2.816 3.7547 0 7.2534 1.3653 3.584 1.28 6.3146 3.9253 2.7307 2.56 4.4374 6.3147 1.7066 3.7547 1.792 8.704 0 1.024-0.768 1.792-0.768 0.68266-1.792 0.68266h-34.219l-1.024-4.608h33.621l-1.1093 1.024v-1.7067q-0.4267-4.0107-2.6453-6.8267-2.2187-2.816-5.376-4.2667-3.072-1.4507-6.4854-1.4507-2.56 0-5.2906 1.024-2.6454 1.024-4.864 3.2427-2.1334 2.1333-3.4987 5.5467-1.3653 3.328-1.3653 7.936 0 5.0347 2.048 9.1307 2.048 4.096 5.888 6.4853 3.84 2.3893 9.3013 2.3893 2.9013 0 5.2907-0.85334 2.3893-0.85333 4.1813-2.2187 1.8773-1.4507 3.072-2.9867 0.9387-0.768 1.792-0.768 0.9387 0 1.536 0.68267 0.6827 0.68267 0.6827 1.536 0 1.024-0.8534 1.792-2.56 3.072-6.656 5.376-4.096 2.2187-8.96 2.2187z"/>
|
||||
<path d="m1345.4 451.18q0 1.024-0.768 1.792t-1.792 0.768q-1.1094 0-1.8774-0.768-0.6826-0.768-0.6826-1.792v-58.027q0-1.024 0.768-1.792t1.792-0.768q1.1093 0 1.792 0.768 0.768 0.768 0.768 1.792z"/>
|
||||
<path d="m1365 451.18q0 1.024-0.768 1.792t-1.792 0.768q-1.1093 0-1.8773-0.768-0.6827-0.768-0.6827-1.792v-58.027q0-1.024 0.768-1.792t1.792-0.768q1.1093 0 1.792 0.768 0.768 0.768 0.768 1.792z"/>
|
||||
<path d="m1410.2 409.37q1.1093 0 1.792 0.768 0.768 0.768 0.768 1.792v37.632q0 6.912-2.7307 11.605-2.7307 4.7787-7.3387 7.168-4.608 2.4747-10.496 2.4747-3.6693 0-6.8266-0.85334-3.072-0.76799-5.0347-2.048-1.024-0.59734-1.536-1.4507t-0.085-1.792q0.4266-1.1947 1.28-1.6213 0.9386-0.34134 1.8773 0.0853 1.4507 0.768 4.1813 1.8773 2.7307 1.1093 6.2294 1.1093 4.6933 0 8.1066-1.9627 3.4987-1.9627 5.376-5.7173 1.8774-3.6693 1.8774-8.7893v-6.144l0.5973 2.048q-1.28 2.6453-3.6693 4.6933-2.304 2.048-5.376 3.2427-2.9867 1.1093-6.4 1.1093-5.12 0-8.5334-2.048-3.328-2.1333-4.9493-5.8027t-1.6213-8.6187v-26.197q0-1.024 0.6826-1.792 0.6827-0.768 1.8774-0.768 1.1093 0 1.792 0.768 0.768 0.768 0.768 1.792v25.429q0 5.9733 2.56 9.216 2.6453 3.2427 8.5333 3.2427 3.6693 0 6.7413-1.7067 3.072-1.792 5.0347-4.608 1.9627-2.9013 1.9627-6.144v-25.429q0-1.024 0.6826-1.792 0.768-0.768 1.8774-0.768z"/>
|
||||
<path d="m1441.9 390.94q1.28 0 2.7306 0.256 1.536 0.256 2.6454 0.93867 1.1093 0.59733 1.1093 1.8773 0 0.93867-0.6827 1.7067-0.6826 0.68266-1.536 0.68266-0.8533 0-2.1333-0.42666-1.28-0.512-2.7307-0.512-1.792 0-3.072 0.85333-1.28 0.768-1.9626 2.304-0.6827 1.4507-0.6827 3.584v48.981q0 1.024-0.768 1.792-0.6827 0.768-1.792 0.768t-1.8773-0.768q-0.6827-0.768-0.6827-1.792v-48.981q0-5.4613 3.1574-8.3627 3.2426-2.9013 8.2773-2.9013zm3.2426 20.053q1.024 0 1.7067 0.68266 0.6827 0.68267 0.6827 1.7067t-0.6827 1.7067q-0.6827 0.68267-1.7067 0.68267h-20.907q-0.9387 0-1.7067-0.68267-0.6826-0.768-0.6826-1.7067 0-1.1093 0.6826-1.7067 0.768-0.68266 1.7067-0.68266zm16.043 40.192q0 1.024-0.768 1.792t-1.792 0.768q-1.1093 0-1.8773-0.768-0.6827-0.768-0.6827-1.792v-39.253q0-1.024 0.6827-1.792 0.768-0.768 1.8773-0.768t1.792 0.768q0.768 0.768 0.768 1.792zm-2.56-47.531q-1.792 0-2.7307-0.768-0.8533-0.85333-0.8533-2.3893v-0.85333q0-1.536 0.9387-2.304 1.024-0.85333 2.7306-0.85333 1.6214 0 2.4747 0.85333 0.9387 0.768 0.9387 2.304v0.85333q0 1.536-0.9387 2.3893-0.8533 0.768-2.56 0.768z"/>
|
||||
<path d="m1495.1 409.11q5.632 0 8.96 2.304 3.4133 2.2187 4.864 6.144 1.5359 3.84 1.5359 8.5333v25.088q0 1.024-0.768 1.792-0.7679 0.768-1.7919 0.768-1.1947 0-1.8774-0.768-0.6826-0.768-0.6826-1.792v-24.832q0-3.4133-1.1094-6.2293-1.1093-2.816-3.6693-4.5227-2.4747-1.7067-6.5707-1.7067-3.6693 0-6.9973 1.7067-3.2427 1.7067-5.2907 4.5227t-2.048 6.2293v24.832q0 1.024-0.768 1.792t-1.792 0.768q-1.1946 0-1.8773-0.768t-0.6827-1.792v-38.229q0-1.024 0.6827-1.792 0.768-0.768 1.8773-0.768 1.1094 0 1.792 0.768 0.768 0.768 0.768 1.792v7.168l-1.9626 3.072q0.1706-2.7307 1.7066-5.2053 1.6214-2.56 4.096-4.5227 2.4747-2.048 5.4614-3.1573 3.072-1.1947 6.144-1.1947z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 12 KiB |
BIN
images/findroid-banner.png
Executable file
BIN
images/findroid-banner.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
|
@ -1,2 +0,0 @@
|
|||
rootProject.name = "Jellyfin"
|
||||
include ':app'
|
1
settings.gradle.kts
Normal file
1
settings.gradle.kts
Normal file
|
@ -0,0 +1 @@
|
|||
include(":app")
|
Loading…
Reference in a new issue