forked from recloudstream/cloudstream
		
	added custom subtitles
This commit is contained in:
		
							parent
							
								
									914238597e
								
							
						
					
					
						commit
						4ec287d1e9
					
				
					 4 changed files with 246 additions and 152 deletions
				
			
		|  | @ -29,6 +29,7 @@ import android.view.animation.Animation | ||||||
| import android.view.animation.AnimationUtils | import android.view.animation.AnimationUtils | ||||||
| import android.widget.* | import android.widget.* | ||||||
| import android.widget.Toast.LENGTH_SHORT | import android.widget.Toast.LENGTH_SHORT | ||||||
|  | import androidx.activity.result.contract.ActivityResultContracts | ||||||
| import androidx.appcompat.app.AlertDialog | import androidx.appcompat.app.AlertDialog | ||||||
| import androidx.core.graphics.blue | import androidx.core.graphics.blue | ||||||
| import androidx.core.graphics.green | import androidx.core.graphics.green | ||||||
|  | @ -47,7 +48,7 @@ import com.fasterxml.jackson.module.kotlin.readValue | ||||||
| import com.google.android.exoplayer2.* | import com.google.android.exoplayer2.* | ||||||
| import com.google.android.exoplayer2.C.TIME_UNSET | import com.google.android.exoplayer2.C.TIME_UNSET | ||||||
| import com.google.android.exoplayer2.database.StandaloneDatabaseProvider | import com.google.android.exoplayer2.database.StandaloneDatabaseProvider | ||||||
| import com.google.android.exoplayer2.source.DefaultMediaSourceFactory | import com.google.android.exoplayer2.source.* | ||||||
| import com.google.android.exoplayer2.trackselection.DefaultTrackSelector | import com.google.android.exoplayer2.trackselection.DefaultTrackSelector | ||||||
| import com.google.android.exoplayer2.ui.AspectRatioFrameLayout | import com.google.android.exoplayer2.ui.AspectRatioFrameLayout | ||||||
| import com.google.android.exoplayer2.ui.SubtitleView | import com.google.android.exoplayer2.ui.SubtitleView | ||||||
|  | @ -63,6 +64,7 @@ import com.google.android.gms.cast.framework.CastButtonFactory | ||||||
| import com.google.android.gms.cast.framework.CastContext | import com.google.android.gms.cast.framework.CastContext | ||||||
| import com.google.android.gms.cast.framework.CastState | import com.google.android.gms.cast.framework.CastState | ||||||
| import com.google.android.material.button.MaterialButton | import com.google.android.material.button.MaterialButton | ||||||
|  | import com.hippo.unifile.UniFile | ||||||
| import com.lagradost.cloudstream3.* | import com.lagradost.cloudstream3.* | ||||||
| import com.lagradost.cloudstream3.MainActivity.Companion.canEnterPipMode | import com.lagradost.cloudstream3.MainActivity.Companion.canEnterPipMode | ||||||
| import com.lagradost.cloudstream3.MainActivity.Companion.getCastSession | import com.lagradost.cloudstream3.MainActivity.Companion.getCastSession | ||||||
|  | @ -100,6 +102,7 @@ import com.lagradost.cloudstream3.utils.VideoDownloadManager.getId | ||||||
| import kotlinx.android.synthetic.main.fragment_player.* | import kotlinx.android.synthetic.main.fragment_player.* | ||||||
| import kotlinx.android.synthetic.main.player_custom_layout.* | import kotlinx.android.synthetic.main.player_custom_layout.* | ||||||
| import kotlinx.coroutines.* | import kotlinx.coroutines.* | ||||||
|  | import okhttp3.internal.format | ||||||
| import java.io.File | import java.io.File | ||||||
| import javax.net.ssl.HttpsURLConnection | import javax.net.ssl.HttpsURLConnection | ||||||
| import javax.net.ssl.SSLContext | import javax.net.ssl.SSLContext | ||||||
|  | @ -766,6 +769,32 @@ class PlayerFragment : Fragment() { | ||||||
|         safeReleasePlayer() |         safeReleasePlayer() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // Open file picker | ||||||
|  |     private val subsPathPicker = | ||||||
|  |         registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> | ||||||
|  |             // It lies, it can be null if file manager quits. | ||||||
|  |             if (uri == null) return@registerForActivityResult | ||||||
|  |             val context = context ?: AcraApplication.context ?: return@registerForActivityResult | ||||||
|  |             // RW perms for the path | ||||||
|  |             val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or | ||||||
|  |                     Intent.FLAG_GRANT_WRITE_URI_PERMISSION | ||||||
|  | 
 | ||||||
|  |             context.contentResolver.takePersistableUriPermission(uri, flags) | ||||||
|  | 
 | ||||||
|  |             val file = UniFile.fromUri(context, uri) | ||||||
|  |             println("Selected URI path: $uri - Full path: ${file.filePath}") | ||||||
|  |             // DO NOT REMOVE THE FILE EXTENSION FROM NAME, IT'S NEEDED FOR MIME TYPES | ||||||
|  |             val name = file.name ?: uri.toString() | ||||||
|  | 
 | ||||||
|  |             viewModel.loadSubtitleFile(uri, name, getEpisode()?.id) | ||||||
|  |             setPreferredSubLanguage(name) | ||||||
|  |             showToast( | ||||||
|  |                 activity, | ||||||
|  |                 format(context.getString(R.string.player_loaded_subtitles), name), | ||||||
|  |                 1000 | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|     private class SettingsContentObserver(handler: Handler?, val activity: Activity) : |     private class SettingsContentObserver(handler: Handler?, val activity: Activity) : | ||||||
|         ContentObserver(handler) { |         ContentObserver(handler) { | ||||||
|         private val audioManager = activity.getSystemService(AUDIO_SERVICE) as? AudioManager |         private val audioManager = activity.getSystemService(AUDIO_SERVICE) as? AudioManager | ||||||
|  | @ -1231,12 +1260,30 @@ class PlayerFragment : Fragment() { | ||||||
|                             sourceDialog.findViewById<MaterialButton>(R.id.cancel_btt)!! |                             sourceDialog.findViewById<MaterialButton>(R.id.cancel_btt)!! | ||||||
|                         val subsSettings = sourceDialog.findViewById<View>(R.id.subs_settings)!! |                         val subsSettings = sourceDialog.findViewById<View>(R.id.subs_settings)!! | ||||||
| 
 | 
 | ||||||
|  |                         val subtitleLoadButton = | ||||||
|  |                             sourceDialog.findViewById<MaterialButton>(R.id.load_btt)!! | ||||||
|  | 
 | ||||||
|  |                         subtitleLoadButton.setOnClickListener { | ||||||
|  | //                            "vtt" -> "text/vtt" | ||||||
|  | //                            "srt" -> "application/x-subrip"// "text/plain" | ||||||
|  |                             subsPathPicker.launch( | ||||||
|  |                                 arrayOf( | ||||||
|  |                                     "text/vtt", | ||||||
|  |                                     "application/x-subrip", | ||||||
|  |                                     "text/plain", | ||||||
|  |                                     "text/str", | ||||||
|  |                                     "application/octet-stream" | ||||||
|  |                                 ) | ||||||
|  |                             ) | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|                         subsSettings.setOnClickListener { |                         subsSettings.setOnClickListener { | ||||||
|                             autoHide() |                             autoHide() | ||||||
|                             saveArguments() |                             saveArguments() | ||||||
|                             SubtitlesFragment.push(activity) |                             SubtitlesFragment.push(activity) | ||||||
|                             sourceDialog.dismissSafe(activity) |                             sourceDialog.dismissSafe(activity) | ||||||
|                         } |                         } | ||||||
|  | 
 | ||||||
|                         var sourceIndex = 0 |                         var sourceIndex = 0 | ||||||
|                         var startSource = 0 |                         var startSource = 0 | ||||||
|                         var sources: List<ExtractorLink> = emptyList() |                         var sources: List<ExtractorLink> = emptyList() | ||||||
|  | @ -2116,40 +2163,7 @@ class PlayerFragment : Fragment() { | ||||||
|                 mediaItemBuilder.setUri(uriPrimary) |                 mediaItemBuilder.setUri(uriPrimary) | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             val subs = context?.getSubs() ?: emptyList() |             fun getDataSourceFactory(isOnline: Boolean): DataSource.Factory { | ||||||
|             val subItems = ArrayList<MediaItem.SubtitleConfiguration>() |  | ||||||
|             val subItemsId = ArrayList<String>() |  | ||||||
| 
 |  | ||||||
|             for (sub in sortSubs(subs)) { |  | ||||||
|                 val langId = |  | ||||||
|                     sub.lang.trimEnd() //SubtitleHelper.fromLanguageToTwoLetters(it.lang) ?: it.lang |  | ||||||
|                 subItemsId.add(langId) |  | ||||||
|                 subItems.add( |  | ||||||
|                     MediaItem.SubtitleConfiguration.Builder(Uri.parse(sub.url)) |  | ||||||
|                         .setMimeType(sub.url.toSubtitleMimeType()) |  | ||||||
|                         .setLanguage("_$langId") |  | ||||||
|                         .setSelectionFlags(C.SELECTION_FLAG_DEFAULT) |  | ||||||
|                         .build() |  | ||||||
|                 ) |  | ||||||
| 
 |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             activeSubtitles = subItemsId |  | ||||||
|             mediaItemBuilder.setSubtitleConfigurations(subItems) |  | ||||||
| 
 |  | ||||||
|             //might add https://github.com/ed828a/Aihua/blob/1896f46888b5a954b367e83f40b845ce174a2328/app/src/main/java/com/dew/aihua/player/playerUI/VideoPlayer.kt#L287 toggle caps |  | ||||||
| 
 |  | ||||||
|             val mediaItem = mediaItemBuilder.build() |  | ||||||
|             val trackSelector = DefaultTrackSelector(requireContext()) |  | ||||||
|             // Disable subtitles |  | ||||||
|             trackSelector.parameters = DefaultTrackSelector.ParametersBuilder(requireContext()) |  | ||||||
|                 // .setRendererDisabled(C.TRACK_TYPE_VIDEO, true) |  | ||||||
|                 .setRendererDisabled(C.TRACK_TYPE_TEXT, true) |  | ||||||
|                 .setDisabledTextTrackSelectionFlags(C.TRACK_TYPE_TEXT) |  | ||||||
|                 .clearSelectionOverrides() |  | ||||||
|                 .build() |  | ||||||
| 
 |  | ||||||
|             fun getDataSourceFactory(): DataSource.Factory { |  | ||||||
|                 return if (isOnline) { |                 return if (isOnline) { | ||||||
|                     DefaultHttpDataSource.Factory().apply { |                     DefaultHttpDataSource.Factory().apply { | ||||||
|                         setUserAgent(USER_AGENT) |                         setUserAgent(USER_AGENT) | ||||||
|  | @ -2174,6 +2188,41 @@ class PlayerFragment : Fragment() { | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             val subs = context?.getSubs() ?: emptyList() | ||||||
|  |             val subItemsId = ArrayList<String>() | ||||||
|  | 
 | ||||||
|  |             val subSources = sortSubs(subs).map { sub -> | ||||||
|  |                 // The url can look like .../document/4294 when the name is EnglishSDH.srt | ||||||
|  |                 val subtitleMimeType = | ||||||
|  |                     if (sub.url.startsWith("content")) sub.lang.toSubtitleMimeType() else sub.url.toSubtitleMimeType() | ||||||
|  | 
 | ||||||
|  |                 val langId = | ||||||
|  |                     sub.lang.trimEnd() //SubtitleHelper.fromLanguageToTwoLetters(it.lang) ?: it.lang | ||||||
|  |                 subItemsId.add(langId) | ||||||
|  |                 val subConfig = MediaItem.SubtitleConfiguration.Builder(Uri.parse(sub.url)) | ||||||
|  |                     .setMimeType(subtitleMimeType) | ||||||
|  |                     .setLanguage("_$langId") | ||||||
|  |                     .setSelectionFlags(C.SELECTION_FLAG_DEFAULT) | ||||||
|  |                     .build() | ||||||
|  |                 SingleSampleMediaSource.Factory(getDataSourceFactory(!sub.url.startsWith("content"))) | ||||||
|  |                     .createMediaSource(subConfig, TIME_UNSET) | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             activeSubtitles = subItemsId | ||||||
|  | //            mediaItemBuilder.setSubtitleConfigurations(subItems) | ||||||
|  | 
 | ||||||
|  |             //might add https://github.com/ed828a/Aihua/blob/1896f46888b5a954b367e83f40b845ce174a2328/app/src/main/java/com/dew/aihua/player/playerUI/VideoPlayer.kt#L287 toggle caps | ||||||
|  | 
 | ||||||
|  |             val mediaItem = mediaItemBuilder.build() | ||||||
|  |             val trackSelector = DefaultTrackSelector(requireContext()) | ||||||
|  |             // Disable subtitles | ||||||
|  |             trackSelector.parameters = DefaultTrackSelector.ParametersBuilder(requireContext()) | ||||||
|  |                 // .setRendererDisabled(C.TRACK_TYPE_VIDEO, true) | ||||||
|  |                 .setRendererDisabled(C.TRACK_TYPE_TEXT, true) | ||||||
|  |                 .setDisabledTextTrackSelectionFlags(C.TRACK_TYPE_TEXT) | ||||||
|  |                 .clearSelectionOverrides() | ||||||
|  |                 .build() | ||||||
|  | 
 | ||||||
|             normalSafeApiCall { |             normalSafeApiCall { | ||||||
|                 val databaseProvider = StandaloneDatabaseProvider(requireContext()) |                 val databaseProvider = StandaloneDatabaseProvider(requireContext()) | ||||||
|                 simpleCache = SimpleCache( |                 simpleCache = SimpleCache( | ||||||
|  | @ -2187,18 +2236,23 @@ class PlayerFragment : Fragment() { | ||||||
| 
 | 
 | ||||||
|             val cacheFactory = CacheDataSource.Factory().apply { |             val cacheFactory = CacheDataSource.Factory().apply { | ||||||
|                 simpleCache?.let { setCache(it) } |                 simpleCache?.let { setCache(it) } | ||||||
|                 setUpstreamDataSourceFactory(getDataSourceFactory()) |                 setUpstreamDataSourceFactory(getDataSourceFactory(isOnline)) | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             val exoPlayerBuilder = |             val exoPlayerBuilder = | ||||||
|                 ExoPlayer.Builder(requireContext()) |                 ExoPlayer.Builder(requireContext()) | ||||||
|                     .setTrackSelector(trackSelector) |                     .setTrackSelector(trackSelector) | ||||||
| 
 | 
 | ||||||
|  |             val videoMediaSource = | ||||||
|  |                 DefaultMediaSourceFactory(cacheFactory).createMediaSource(mediaItem) | ||||||
|  | 
 | ||||||
|             exoPlayer = exoPlayerBuilder.build().apply { |             exoPlayer = exoPlayerBuilder.build().apply { | ||||||
|                 playWhenReady = isPlayerPlaying |                 playWhenReady = isPlayerPlaying | ||||||
|                 seekTo(currentWindow, playbackPosition) |                 seekTo(currentWindow, playbackPosition) | ||||||
|                 setMediaSource( |                 setMediaSource( | ||||||
|                     DefaultMediaSourceFactory(cacheFactory).createMediaSource(mediaItem), |                     MergingMediaSource( | ||||||
|  |                         videoMediaSource, *subSources.toTypedArray() | ||||||
|  |                     ), | ||||||
|                     playbackPosition |                     playbackPosition | ||||||
|                 ) |                 ) | ||||||
|                 prepare() |                 prepare() | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| package com.lagradost.cloudstream3.ui.result | package com.lagradost.cloudstream3.ui.result | ||||||
| 
 | 
 | ||||||
| import android.content.Context | import android.content.Context | ||||||
|  | import android.net.Uri | ||||||
| import androidx.lifecycle.LiveData | import androidx.lifecycle.LiveData | ||||||
| import androidx.lifecycle.MutableLiveData | import androidx.lifecycle.MutableLiveData | ||||||
| import androidx.lifecycle.ViewModel | import androidx.lifecycle.ViewModel | ||||||
|  | @ -57,7 +58,8 @@ class ResultViewModel : ViewModel() { | ||||||
| 
 | 
 | ||||||
|     private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData() |     private val _resultResponse: MutableLiveData<Resource<Any?>> = MutableLiveData() | ||||||
|     private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData() |     private val _episodes: MutableLiveData<List<ResultEpisode>> = MutableLiveData() | ||||||
|     private val episodeById: MutableLiveData<HashMap<Int, Int>> = MutableLiveData() // lookup by ID to get Index |     private val episodeById: MutableLiveData<HashMap<Int, Int>> = | ||||||
|  |         MutableLiveData() // lookup by ID to get Index | ||||||
| 
 | 
 | ||||||
|     private val _publicEpisodes: MutableLiveData<Resource<List<ResultEpisode>>> = MutableLiveData() |     private val _publicEpisodes: MutableLiveData<Resource<List<ResultEpisode>>> = MutableLiveData() | ||||||
|     private val _publicEpisodesCount: MutableLiveData<Int> = MutableLiveData() // before the sorting |     private val _publicEpisodesCount: MutableLiveData<Int> = MutableLiveData() // before the sorting | ||||||
|  | @ -83,7 +85,8 @@ class ResultViewModel : ViewModel() { | ||||||
|     private val _dubSubSelections: MutableLiveData<Set<DubStatus>> = MutableLiveData() |     private val _dubSubSelections: MutableLiveData<Set<DubStatus>> = MutableLiveData() | ||||||
| 
 | 
 | ||||||
|     val dubSubEpisodes: LiveData<Map<DubStatus, List<ResultEpisode>>?> get() = _dubSubEpisodes |     val dubSubEpisodes: LiveData<Map<DubStatus, List<ResultEpisode>>?> get() = _dubSubEpisodes | ||||||
|     private val _dubSubEpisodes: MutableLiveData<Map<DubStatus, List<ResultEpisode>>?> = MutableLiveData() |     private val _dubSubEpisodes: MutableLiveData<Map<DubStatus, List<ResultEpisode>>?> = | ||||||
|  |         MutableLiveData() | ||||||
| 
 | 
 | ||||||
|     private val _watchStatus: MutableLiveData<WatchType> = MutableLiveData() |     private val _watchStatus: MutableLiveData<WatchType> = MutableLiveData() | ||||||
|     val watchStatus: LiveData<WatchType> get() = _watchStatus |     val watchStatus: LiveData<WatchType> get() = _watchStatus | ||||||
|  | @ -291,7 +294,14 @@ class ResultViewModel : ViewModel() { | ||||||
|         _apiName.postValue(apiName) |         _apiName.postValue(apiName) | ||||||
|         val api = getApiFromNameNull(apiName) |         val api = getApiFromNameNull(apiName) | ||||||
|         if (api == null) { |         if (api == null) { | ||||||
|             _resultResponse.postValue(Resource.Failure(false, null, null, "This provider does not exist")) |             _resultResponse.postValue( | ||||||
|  |                 Resource.Failure( | ||||||
|  |                     false, | ||||||
|  |                     null, | ||||||
|  |                     null, | ||||||
|  |                     "This provider does not exist" | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|             return@launch |             return@launch | ||||||
|         } |         } | ||||||
|         repo = APIRepository(api) |         repo = APIRepository(api) | ||||||
|  | @ -335,7 +345,8 @@ class ResultViewModel : ViewModel() { | ||||||
|                         _dubStatus.postValue(dubStatus) |                         _dubStatus.postValue(dubStatus) | ||||||
| 
 | 
 | ||||||
|                         _dubSubSelections.postValue(d.episodes.keys) |                         _dubSubSelections.postValue(d.episodes.keys) | ||||||
|                         val fillerEpisodes = if (showFillers) safeApiCall { getFillerEpisodes(d.name) } else null |                         val fillerEpisodes = | ||||||
|  |                             if (showFillers) safeApiCall { getFillerEpisodes(d.name) } else null | ||||||
| 
 | 
 | ||||||
|                         var idIndex = 0 |                         var idIndex = 0 | ||||||
|                         val res = d.episodes.map { ep -> |                         val res = d.episodes.map { ep -> | ||||||
|  | @ -464,6 +475,20 @@ class ResultViewModel : ViewModel() { | ||||||
|         return loadEpisode(episode.id, episode.data, isCasting) |         return loadEpisode(episode.id, episode.data, isCasting) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     fun loadSubtitleFile(uri: Uri, name: String, id: Int?) { | ||||||
|  |         if (id == null) return | ||||||
|  |         val hashMap: HashMap<String, SubtitleFile> = _allEpisodesSubs.value?.get(id) ?: hashMapOf() | ||||||
|  |         hashMap[name] = SubtitleFile( | ||||||
|  |             name, | ||||||
|  |             uri.toString() | ||||||
|  |         ) | ||||||
|  |         _allEpisodesSubs.value.apply { | ||||||
|  |             this?.set(id, hashMap) | ||||||
|  |         }?.let { | ||||||
|  |             _allEpisodesSubs.postValue(it) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     private suspend fun loadEpisode( |     private suspend fun loadEpisode( | ||||||
|         id: Int, |         id: Int, | ||||||
|         data: String, |         data: String, | ||||||
|  |  | ||||||
|  | @ -1,137 +1,150 @@ | ||||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||||
| <LinearLayout | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|         xmlns:android="http://schemas.android.com/apk/res/android" |  | ||||||
|     xmlns:tools="http://schemas.android.com/tools" |     xmlns:tools="http://schemas.android.com/tools" | ||||||
|         android:orientation="vertical" |  | ||||||
|         android:layout_width="match_parent" |  | ||||||
|         android:background="@null" |  | ||||||
|         android:layout_height="match_parent"> |  | ||||||
|     <LinearLayout |  | ||||||
|             android:orientation="horizontal" |  | ||||||
|             android:layout_marginBottom="60dp" |  | ||||||
|     android:layout_width="match_parent" |     android:layout_width="match_parent" | ||||||
|     android:layout_height="match_parent" |     android:layout_height="match_parent" | ||||||
|             android:baselineAligned="false"> |     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||||
|  |     android:background="@null" | ||||||
|  |     android:orientation="vertical"> | ||||||
|  | 
 | ||||||
|  |     <LinearLayout | ||||||
|  |         android:layout_width="match_parent" | ||||||
|  |         android:layout_height="match_parent" | ||||||
|  |         android:layout_marginBottom="60dp" | ||||||
|  |         android:baselineAligned="false" | ||||||
|  |         android:orientation="horizontal"> | ||||||
| 
 | 
 | ||||||
|         <LinearLayout |         <LinearLayout | ||||||
|             android:id="@+id/sort_sources_holder" |             android:id="@+id/sort_sources_holder" | ||||||
|             android:layout_width="0dp" |             android:layout_width="0dp" | ||||||
|             android:layout_height="wrap_content" |             android:layout_height="wrap_content" | ||||||
|                 android:orientation="vertical" |             android:layout_weight="50" | ||||||
|                 android:layout_weight="50"> |             android:orientation="vertical"> | ||||||
|             <TextView |  | ||||||
|                     android:layout_marginTop="10dp" |  | ||||||
|                     android:paddingTop="10dp" |  | ||||||
|                     android:paddingBottom="10dp" |  | ||||||
|                     android:paddingStart="?android:attr/listPreferredItemPaddingStart" |  | ||||||
|                     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" |  | ||||||
|                     android:textStyle="bold" |  | ||||||
|                     android:text="@string/pick_source" |  | ||||||
|                     android:textSize="20sp" |  | ||||||
|                     android:textColor="?attr/textColor" |  | ||||||
|                     android:layout_width="match_parent" |  | ||||||
|                     android:layout_rowWeight="1" |  | ||||||
|                     android:layout_height="wrap_content"> |  | ||||||
|             </TextView> |  | ||||||
|             <ListView |  | ||||||
|                     android:nextFocusRight="@id/apply_btt" |  | ||||||
|                     android:nextFocusLeft="@id/sort_subtitles" |  | ||||||
| 
 | 
 | ||||||
|                     android:id="@+id/sort_providers" |             <TextView | ||||||
|                     android:background="?attr/primaryBlackBackground" |  | ||||||
|                     android:requiresFadingEdge="vertical" |  | ||||||
|                     tools:listitem="@layout/sort_bottom_single_choice" |  | ||||||
|                 android:layout_width="match_parent" |                 android:layout_width="match_parent" | ||||||
|  |                 android:layout_height="wrap_content" | ||||||
|  |                 android:layout_rowWeight="1" | ||||||
|  |                 android:layout_marginTop="10dp" | ||||||
|  |                 android:paddingStart="?android:attr/listPreferredItemPaddingStart" | ||||||
|  |                 android:paddingTop="10dp" | ||||||
|  |                 android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" | ||||||
|  |                 android:paddingBottom="10dp" | ||||||
|  |                 android:text="@string/pick_source" | ||||||
|  |                 android:textColor="?attr/textColor" | ||||||
|  |                 android:textSize="20sp" | ||||||
|  |                 android:textStyle="bold" /> | ||||||
|  | 
 | ||||||
|  |             <ListView | ||||||
|  |                 android:id="@+id/sort_providers" | ||||||
|  |                 android:layout_width="match_parent" | ||||||
|  | 
 | ||||||
|                 android:layout_height="match_parent" |                 android:layout_height="match_parent" | ||||||
|                 android:layout_rowWeight="1" |                 android:layout_rowWeight="1" | ||||||
|             /> |                 android:background="?attr/primaryBlackBackground" | ||||||
|  |                 android:nextFocusLeft="@id/sort_subtitles" | ||||||
|  |                 android:nextFocusRight="@id/apply_btt" | ||||||
|  |                 tools:listitem="@layout/sort_bottom_single_choice" /> | ||||||
|         </LinearLayout> |         </LinearLayout> | ||||||
|  | 
 | ||||||
|         <LinearLayout |         <LinearLayout | ||||||
|             android:id="@+id/sort_subtitles_holder" |             android:id="@+id/sort_subtitles_holder" | ||||||
|             android:layout_width="0dp" |             android:layout_width="0dp" | ||||||
|             android:layout_height="wrap_content" |             android:layout_height="wrap_content" | ||||||
|                 android:orientation="vertical" |             android:layout_weight="50" | ||||||
|                 android:layout_weight="50"> |             android:orientation="vertical"> | ||||||
|             <FrameLayout |  | ||||||
|                     android:foreground="?android:attr/selectableItemBackgroundBorderless" |  | ||||||
|                     android:id="@+id/subs_settings" |  | ||||||
|                     android:orientation="horizontal" |  | ||||||
|                     android:layout_marginTop="10dp" |  | ||||||
|                     android:paddingTop="10dp" |  | ||||||
|                     android:paddingBottom="10dp" |  | ||||||
|                     android:layout_width="match_parent" |  | ||||||
|                     android:layout_height="wrap_content"> |  | ||||||
|                 <TextView |  | ||||||
|                         android:nextFocusRight="@id/sort_providers" |  | ||||||
|                         android:nextFocusDown="@id/sort_subtitles" |  | ||||||
| 
 | 
 | ||||||
|  |             <LinearLayout | ||||||
|  |                 android:id="@+id/subs_settings" | ||||||
|  |                 android:layout_width="match_parent" | ||||||
|  |                 android:layout_height="wrap_content" | ||||||
|  |                 android:layout_marginTop="10dp" | ||||||
|  |                 android:foreground="?android:attr/selectableItemBackgroundBorderless" | ||||||
|  |                 android:orientation="horizontal" | ||||||
|  |                 android:paddingTop="10dp" | ||||||
|  |                 android:paddingBottom="10dp"> | ||||||
|  | 
 | ||||||
|  |                 <TextView | ||||||
|  |                     android:layout_width="0dp" | ||||||
|  |                     android:layout_height="wrap_content" | ||||||
|  |                     android:layout_rowWeight="1" | ||||||
|  | 
 | ||||||
|  |                     android:layout_weight="1" | ||||||
|  |                     android:nextFocusRight="@id/sort_providers" | ||||||
|  | 
 | ||||||
|  |                     android:nextFocusDown="@id/sort_subtitles" | ||||||
|                     android:paddingStart="?android:attr/listPreferredItemPaddingStart" |                     android:paddingStart="?android:attr/listPreferredItemPaddingStart" | ||||||
|                     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" |                     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" | ||||||
| 
 |  | ||||||
|                         android:textStyle="bold" |  | ||||||
|                     android:text="@string/pick_subtitle" |                     android:text="@string/pick_subtitle" | ||||||
|                         android:textSize="20sp" |  | ||||||
|                     android:textColor="?attr/textColor" |                     android:textColor="?attr/textColor" | ||||||
|                         android:layout_rowWeight="1" |                     android:textSize="20sp" | ||||||
|                         android:layout_width="match_parent" |                     android:textStyle="bold" /> | ||||||
|                         android:layout_height="wrap_content"> |  | ||||||
|                 </TextView> |  | ||||||
| 
 | 
 | ||||||
|                 <ImageView |                 <ImageView | ||||||
|                         android:visibility="gone" |  | ||||||
|                         android:layout_marginTop="0dp" |  | ||||||
|                         android:layout_marginEnd="10dp" |  | ||||||
|                         android:layout_gravity="end|center_vertical" |  | ||||||
| 
 |  | ||||||
|                         android:src="@drawable/ic_outline_settings_24" |  | ||||||
|                     android:layout_width="25dp" |                     android:layout_width="25dp" | ||||||
|                     android:layout_height="wrap_content" |                     android:layout_height="wrap_content" | ||||||
|                         android:contentDescription="@string/home_change_provider_img_des"> |                     android:layout_gravity="end|center_vertical" | ||||||
|                 </ImageView> |                     android:layout_marginTop="0dp" | ||||||
|             </FrameLayout> |                     android:layout_marginEnd="10dp" | ||||||
|  | 
 | ||||||
|  |                     android:contentDescription="@string/home_change_provider_img_des" | ||||||
|  |                     android:src="@drawable/ic_outline_settings_24" | ||||||
|  |                     android:visibility="gone" /> | ||||||
|  |             </LinearLayout> | ||||||
| 
 | 
 | ||||||
|             <ListView |             <ListView | ||||||
|                     android:nextFocusRight="@id/cancel_btt" |  | ||||||
|                     android:nextFocusLeft="@id/sort_providers" |  | ||||||
| 
 |  | ||||||
|                 android:id="@+id/sort_subtitles" |                 android:id="@+id/sort_subtitles" | ||||||
|                     android:background="?attr/primaryBlackBackground" |  | ||||||
|                     android:requiresFadingEdge="vertical" |  | ||||||
|                     tools:listitem="@layout/sort_bottom_single_choice" |  | ||||||
|                 android:layout_width="match_parent" |                 android:layout_width="match_parent" | ||||||
|  | 
 | ||||||
|  |                 android:layout_height="match_parent" | ||||||
|                 android:layout_rowWeight="1" |                 android:layout_rowWeight="1" | ||||||
|                     android:layout_height="match_parent"> |                 android:background="?attr/primaryBlackBackground" | ||||||
|             </ListView> |                 android:nextFocusLeft="@id/sort_providers" | ||||||
|  |                 android:nextFocusRight="@id/cancel_btt" | ||||||
|  |                 tools:listitem="@layout/sort_bottom_single_choice" /> | ||||||
|         </LinearLayout> |         </LinearLayout> | ||||||
|     </LinearLayout> |     </LinearLayout> | ||||||
| 
 | 
 | ||||||
|     <LinearLayout |     <LinearLayout | ||||||
|             android:orientation="horizontal" |  | ||||||
|             android:layout_gravity="bottom" |  | ||||||
|             android:gravity="bottom|end" |  | ||||||
|             android:layout_marginTop="-60dp" |  | ||||||
|         android:layout_width="match_parent" |         android:layout_width="match_parent" | ||||||
|             android:layout_height="60dp"> |         android:layout_height="60dp" | ||||||
|  |         android:layout_gravity="bottom" | ||||||
|  |         android:layout_marginTop="-60dp" | ||||||
|  |         android:orientation="horizontal"> | ||||||
|  | 
 | ||||||
|  | <!--        Kinda hack because the gravity won't behave correctly--> | ||||||
|  |         <LinearLayout | ||||||
|  |             android:layout_weight="1" | ||||||
|  |             android:layout_width="0dp" | ||||||
|  |             android:layout_height="wrap_content" | ||||||
|  |             android:layout_gravity="center_vertical|start"> | ||||||
| 
 | 
 | ||||||
|             <com.google.android.material.button.MaterialButton |             <com.google.android.material.button.MaterialButton | ||||||
|                 android:nextFocusRight="@id/cancel_btt" |                 android:id="@+id/load_btt" | ||||||
|  |                 app:icon="@drawable/ic_baseline_subtitles_24" | ||||||
|  |                 style="@style/WhiteButton" | ||||||
|  |                 android:layout_width="wrap_content" | ||||||
|                 android:nextFocusLeft="@id/cancel_btt" |                 android:nextFocusLeft="@id/cancel_btt" | ||||||
|  |                 android:nextFocusRight="@id/apply_btt" | ||||||
|  |                 android:text="@string/player_load_subtitles" /> | ||||||
|  |         </LinearLayout> | ||||||
| 
 | 
 | ||||||
|  |         <com.google.android.material.button.MaterialButton | ||||||
|             android:id="@+id/apply_btt" |             android:id="@+id/apply_btt" | ||||||
|             style="@style/WhiteButton" |             style="@style/WhiteButton" | ||||||
|                 android:layout_gravity="center_vertical|end" |  | ||||||
|                 android:text="@string/sort_apply" |  | ||||||
|             android:layout_width="wrap_content" |             android:layout_width="wrap_content" | ||||||
|         /> |             android:layout_gravity="center_vertical|end" | ||||||
|         <com.google.android.material.button.MaterialButton |             android:nextFocusLeft="@id/cancel_btt" | ||||||
|                 android:nextFocusRight="@id/apply_btt" |             android:nextFocusRight="@id/cancel_btt" | ||||||
|                 android:nextFocusLeft="@id/apply_btt" |             android:text="@string/sort_apply" /> | ||||||
| 
 | 
 | ||||||
|  |         <com.google.android.material.button.MaterialButton | ||||||
|             android:id="@+id/cancel_btt" |             android:id="@+id/cancel_btt" | ||||||
|             style="@style/BlackButton" |             style="@style/BlackButton" | ||||||
|                 android:layout_gravity="center_vertical|end" |  | ||||||
|                 android:text="@string/sort_cancel" |  | ||||||
|             android:layout_width="wrap_content" |             android:layout_width="wrap_content" | ||||||
|         /> |             android:layout_gravity="center_vertical|end" | ||||||
|  |             android:nextFocusLeft="@id/apply_btt" | ||||||
|  |             android:nextFocusRight="@id/apply_btt" | ||||||
|  |             android:text="@string/sort_cancel" /> | ||||||
|     </LinearLayout> |     </LinearLayout> | ||||||
| </LinearLayout> | </LinearLayout> | ||||||
|  |  | ||||||
|  | @ -368,4 +368,6 @@ | ||||||
|     <string name="subtitles_example_text">The quick brown fox jumps over the lazy dog</string> |     <string name="subtitles_example_text">The quick brown fox jumps over the lazy dog</string> | ||||||
| 
 | 
 | ||||||
|     <string name="tab_recommended">Recommended</string> |     <string name="tab_recommended">Recommended</string> | ||||||
|  |     <string name="player_loaded_subtitles">Loaded %s</string> | ||||||
|  |     <string name="player_load_subtitles">Load from file</string> | ||||||
| </resources> | </resources> | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue