mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2026-05-29 19:13:01 +02:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f8f446933c |
@@ -82,9 +82,3 @@
|
||||
[submodule "app/src/stable/assets/sources/dailymotion"]
|
||||
path = app/src/stable/assets/sources/dailymotion
|
||||
url = ../plugins/dailymotion.git
|
||||
[submodule "app/src/stable/assets/sources/apple-podcast"]
|
||||
path = app/src/stable/assets/sources/apple-podcast
|
||||
url = ../plugins/apple-podcasts.git
|
||||
[submodule "app/src/unstable/assets/sources/apple-podcasts"]
|
||||
path = app/src/unstable/assets/sources/apple-podcasts
|
||||
url = ../plugins/apple-podcasts.git
|
||||
|
||||
@@ -36,12 +36,6 @@
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
|
||||
</provider>
|
||||
|
||||
<receiver android:name=".receivers.MediaButtonReceiver" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service android:name=".services.MediaPlaybackService"
|
||||
android:enabled="true"
|
||||
android:foregroundServiceType="mediaPlayback" />
|
||||
|
||||
@@ -412,13 +412,15 @@ class Settings : FragmentedStorageFileJson() {
|
||||
var preferredPreviewQuality: Int = 5;
|
||||
fun getPreferredPreviewQualityPixelCount(): Int = preferedQualityToPixels(preferredPreviewQuality);
|
||||
|
||||
|
||||
@FormField(R.string.simplify_sources, FieldForm.TOGGLE, R.string.simplify_sources_description, 4)
|
||||
var simplifySources: Boolean = true;
|
||||
|
||||
@FormField(R.string.always_allow_reverse_landscape_auto_rotate, FieldForm.TOGGLE, R.string.always_allow_reverse_landscape_auto_rotate_description, 5)
|
||||
var alwaysAllowReverseLandscapeAutoRotate: Boolean = true
|
||||
@FormField(R.string.auto_rotate, FieldForm.DROPDOWN, -1, 5)
|
||||
@DropdownFieldOptionsId(R.array.system_enabled_disabled_array)
|
||||
var autoRotate: Int = 2;
|
||||
|
||||
@FormField(R.string.background_behavior, FieldForm.DROPDOWN, -1, 6)
|
||||
@FormField(R.string.background_behavior, FieldForm.DROPDOWN, -1, 7)
|
||||
@DropdownFieldOptionsId(R.array.player_background_behavior)
|
||||
var backgroundPlay: Int = 2;
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package com.futo.platformplayer.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.media.AudioManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.StrictMode
|
||||
@@ -74,7 +72,6 @@ import com.futo.platformplayer.fragment.mainactivity.topbar.SearchTopBarFragment
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.models.ImportCache
|
||||
import com.futo.platformplayer.models.UrlVideoWithTime
|
||||
import com.futo.platformplayer.receivers.MediaButtonReceiver
|
||||
import com.futo.platformplayer.setNavigationBarColorAndIcons
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.states.StateBackup
|
||||
@@ -110,7 +107,6 @@ import java.util.LinkedList
|
||||
import java.util.Queue
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||
|
||||
//TODO: Move to dimensions
|
||||
|
||||
+3
-3
@@ -66,6 +66,7 @@ class PolycentricCreateProfileActivity : AppCompatActivity() {
|
||||
val username = _profileName.text.toString();
|
||||
if (username.length < 3) {
|
||||
UIDialogs.toast(this@PolycentricCreateProfileActivity, getString(R.string.must_be_at_least_3_characters_long));
|
||||
_creating = false;
|
||||
return@setOnClickListener;
|
||||
}
|
||||
|
||||
@@ -93,8 +94,6 @@ class PolycentricCreateProfileActivity : AppCompatActivity() {
|
||||
} catch (e: Throwable) {
|
||||
Logger.e(TAG, getString(R.string.failed_to_create_profile), e);
|
||||
return@launch;
|
||||
} finally {
|
||||
_creating = false;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -111,6 +110,7 @@ class PolycentricCreateProfileActivity : AppCompatActivity() {
|
||||
_buttonCreate.visibility = View.VISIBLE;
|
||||
_loader.stop();
|
||||
_loader.visibility = View.GONE;
|
||||
_creating = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ class PolycentricCreateProfileActivity : AppCompatActivity() {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
} catch (e: Exception) {
|
||||
_creating = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -122,11 +122,7 @@ class SyncPairActivity : AppCompatActivity() {
|
||||
} catch (e: Throwable) {
|
||||
withContext(Dispatchers.Main) {
|
||||
_layoutPairingError.visibility = View.VISIBLE
|
||||
if(e.message == "Failed to connect") {
|
||||
_textError.text = "Failed to connect.\n\nThis may be due to not being on the same network, due to firewall, or vpn.\nSync currently operates only over local direct connections."
|
||||
}
|
||||
else
|
||||
_textError.text = e.message
|
||||
_textError.text = e.message
|
||||
_layoutPairing.visibility = View.GONE
|
||||
Logger.e(TAG, "Failed to pair", e)
|
||||
}
|
||||
|
||||
@@ -100,7 +100,6 @@ class VideoDownload {
|
||||
|
||||
var requireVideoSource: Boolean = false;
|
||||
var requireAudioSource: Boolean = false;
|
||||
var requiredCheck: Boolean = false;
|
||||
|
||||
@Contextual
|
||||
@Transient
|
||||
@@ -165,7 +164,7 @@ class VideoDownload {
|
||||
onStateChanged.emit(newState);
|
||||
}
|
||||
|
||||
constructor(video: IPlatformVideo, targetPixelCount: Long? = null, targetBitrate: Long? = null, optionalSources: Boolean = false) {
|
||||
constructor(video: IPlatformVideo, targetPixelCount: Long? = null, targetBitrate: Long? = null) {
|
||||
this.video = SerializedPlatformVideo.fromVideo(video);
|
||||
this.videoSource = null;
|
||||
this.audioSource = null;
|
||||
@@ -176,9 +175,8 @@ class VideoDownload {
|
||||
this.requiresLiveVideoSource = false;
|
||||
this.requiresLiveAudioSource = false;
|
||||
this.targetVideoName = videoSource?.name;
|
||||
this.requireVideoSource = targetPixelCount != null;
|
||||
this.requireVideoSource = targetPixelCount != null
|
||||
this.requireAudioSource = targetBitrate != null; //TODO: May not be a valid check.. can only be determined after live fetch?
|
||||
this.requiredCheck = optionalSources;
|
||||
}
|
||||
constructor(video: IPlatformVideoDetails, videoSource: IVideoSource?, audioSource: IAudioSource?, subtitleSource: SubtitleRawSource?) {
|
||||
this.video = SerializedPlatformVideo.fromVideo(video);
|
||||
@@ -252,30 +250,6 @@ class VideoDownload {
|
||||
if(original !is IPlatformVideoDetails)
|
||||
throw IllegalStateException("Original content is not media?");
|
||||
|
||||
if(requiredCheck) {
|
||||
if(original.video is VideoUnMuxedSourceDescriptor) {
|
||||
if(requireVideoSource) {
|
||||
if((original.video as VideoUnMuxedSourceDescriptor).audioSources.any() && !original.video.videoSources.any()) {
|
||||
requireVideoSource = false;
|
||||
targetPixelCount = null;
|
||||
}
|
||||
}
|
||||
if(requireAudioSource) {
|
||||
if(!(original.video as VideoUnMuxedSourceDescriptor).audioSources.any() && original.video.videoSources.any()) {
|
||||
requireAudioSource = false;
|
||||
targetBitrate = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(requireAudioSource) {
|
||||
requireAudioSource = false;
|
||||
targetBitrate = null;
|
||||
}
|
||||
}
|
||||
requiredCheck = false;
|
||||
}
|
||||
|
||||
if(original.video.hasAnySource() && !original.isDownloadable()) {
|
||||
Logger.i(TAG, "Attempted to download unsupported video [${original.name}]:${original.url}");
|
||||
throw DownloadException("Unsupported video for downloading", false);
|
||||
|
||||
+6
-15
@@ -1,13 +1,12 @@
|
||||
package com.futo.platformplayer.fragment.channel.tab
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.Settings
|
||||
@@ -16,6 +15,7 @@ import com.futo.platformplayer.api.media.models.PlatformAuthorLink
|
||||
import com.futo.platformplayer.api.media.models.channels.IPlatformChannel
|
||||
import com.futo.platformplayer.api.media.models.contents.ContentType
|
||||
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
|
||||
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
|
||||
import com.futo.platformplayer.api.media.platforms.js.models.JSPager
|
||||
import com.futo.platformplayer.api.media.structures.IAsyncPager
|
||||
import com.futo.platformplayer.api.media.structures.IPager
|
||||
@@ -41,11 +41,10 @@ import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
||||
import com.futo.platformplayer.views.adapters.feedtypes.PreviewContentListAdapter
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.max
|
||||
|
||||
class ChannelContentsFragment : Fragment(), IChannelTabFragment {
|
||||
private var _recyclerResults: RecyclerView? = null;
|
||||
private var _glmVideo: GridLayoutManager? = null;
|
||||
private var _llmVideo: LinearLayoutManager? = null;
|
||||
private var _loading = false;
|
||||
private var _pager_parent: IPager<IPlatformContent>? = null;
|
||||
private var _pager: IPager<IPlatformContent>? = null;
|
||||
@@ -119,7 +118,7 @@ class ChannelContentsFragment : Fragment(), IChannelTabFragment {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
|
||||
val recyclerResults = _recyclerResults ?: return;
|
||||
val llmVideo = _glmVideo ?: return;
|
||||
val llmVideo = _llmVideo ?: return;
|
||||
|
||||
val visibleItemCount = recyclerResults.childCount;
|
||||
val firstVisibleItem = llmVideo.findFirstVisibleItemPosition();
|
||||
@@ -164,10 +163,9 @@ class ChannelContentsFragment : Fragment(), IChannelTabFragment {
|
||||
this.onLongPress.subscribe(this@ChannelContentsFragment.onLongPress::emit);
|
||||
}
|
||||
|
||||
val numColumns = max((resources.configuration.screenWidthDp.toDouble() / resources.getInteger(R.integer.column_width_dp)).toInt(), 1)
|
||||
_glmVideo = GridLayoutManager(view.context, numColumns);
|
||||
_llmVideo = LinearLayoutManager(view.context);
|
||||
_recyclerResults?.adapter = _adapterResults;
|
||||
_recyclerResults?.layoutManager = _glmVideo;
|
||||
_recyclerResults?.layoutManager = _llmVideo;
|
||||
_recyclerResults?.addOnScrollListener(_scrollListener);
|
||||
|
||||
return view;
|
||||
@@ -183,13 +181,6 @@ class ChannelContentsFragment : Fragment(), IChannelTabFragment {
|
||||
_nextPageHandler.cancel();
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
|
||||
_glmVideo?.spanCount =
|
||||
max((resources.configuration.screenWidthDp.toDouble() / resources.getInteger(R.integer.column_width_dp)).toInt(), 1)
|
||||
}
|
||||
|
||||
/*
|
||||
private fun setPager(pager: IPager<IPlatformContent>, cache: FeedFragment.ItemCache<IPlatformContent>? = null) {
|
||||
if (_pager_parent != null && _pager_parent is IRefreshPager<*>) {
|
||||
|
||||
+5
-15
@@ -1,13 +1,12 @@
|
||||
package com.futo.platformplayer.fragment.channel.tab
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.Settings
|
||||
@@ -37,11 +36,10 @@ import com.futo.platformplayer.views.adapters.InsertedViewAdapterWithLoader
|
||||
import com.futo.platformplayer.views.adapters.feedtypes.PreviewContentListAdapter
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.max
|
||||
|
||||
class ChannelPlaylistsFragment : Fragment(), IChannelTabFragment {
|
||||
private var _recyclerResults: RecyclerView? = null
|
||||
private var _glmPlaylist: GridLayoutManager? = null
|
||||
private var _llmPlaylist: LinearLayoutManager? = null
|
||||
private var _loading = false
|
||||
private var _pagerParent: IPager<IPlatformPlaylist>? = null
|
||||
private var _pager: IPager<IPlatformPlaylist>? = null
|
||||
@@ -111,7 +109,7 @@ class ChannelPlaylistsFragment : Fragment(), IChannelTabFragment {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
|
||||
val recyclerResults = _recyclerResults ?: return
|
||||
val llmPlaylist = _glmPlaylist ?: return
|
||||
val llmPlaylist = _llmPlaylist ?: return
|
||||
|
||||
val visibleItemCount = recyclerResults.childCount
|
||||
val firstVisibleItem = llmPlaylist.findFirstVisibleItemPosition()
|
||||
@@ -160,10 +158,9 @@ class ChannelPlaylistsFragment : Fragment(), IChannelTabFragment {
|
||||
this.onLongPress.subscribe(this@ChannelPlaylistsFragment.onLongPress::emit)
|
||||
}
|
||||
|
||||
val numColumns = max((resources.configuration.screenWidthDp.toDouble() / resources.getInteger(R.integer.column_width_dp)).toInt(), 1)
|
||||
_glmPlaylist = GridLayoutManager(view.context, numColumns)
|
||||
_llmPlaylist = LinearLayoutManager(view.context)
|
||||
_recyclerResults?.adapter = _adapterResults
|
||||
_recyclerResults?.layoutManager = _glmPlaylist
|
||||
_recyclerResults?.layoutManager = _llmPlaylist
|
||||
_recyclerResults?.addOnScrollListener(_scrollListener)
|
||||
|
||||
return view
|
||||
@@ -179,13 +176,6 @@ class ChannelPlaylistsFragment : Fragment(), IChannelTabFragment {
|
||||
_nextPageHandler.cancel()
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
|
||||
_glmPlaylist?.spanCount =
|
||||
max((resources.configuration.screenWidthDp.toDouble() / resources.getInteger(R.integer.column_width_dp)).toInt(), 1)
|
||||
}
|
||||
|
||||
private fun setPager(
|
||||
pager: IPager<IPlatformPlaylist>
|
||||
) {
|
||||
|
||||
@@ -90,7 +90,7 @@ class BuyFragment : MainFragment() {
|
||||
try {
|
||||
val currencies = StatePayment.instance.getAvailableCurrencies("grayjay");
|
||||
val prices = StatePayment.instance.getAvailableCurrencyPrices("grayjay");
|
||||
val country = StatePayment.instance.getPaymentCountryFromIP(true)?.let { c -> PaymentConfigurations.COUNTRIES.find { it.id.equals(c, ignoreCase = true) } };
|
||||
val country = StatePayment.instance.getPaymentCountryFromIP()?.let { c -> PaymentConfigurations.COUNTRIES.find { it.id.equals(c, ignoreCase = true) } };
|
||||
val currency = country?.let { c -> PaymentConfigurations.CURRENCIES.find { it.id == c.defaultCurrencyId && (currencies.contains(it.id)) } };
|
||||
|
||||
if(currency != null && prices.containsKey(currency.id)) {
|
||||
|
||||
+1
-2
@@ -33,7 +33,6 @@ import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuItem
|
||||
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuOverlay
|
||||
import com.futo.platformplayer.withTimestamp
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.max
|
||||
|
||||
abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent, IPlatformContent, IPager<IPlatformContent>, ContentPreviewViewHolder> where TFragment : MainFragment {
|
||||
private var _exoPlayer: PlayerManager? = null;
|
||||
@@ -169,7 +168,7 @@ abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent
|
||||
val glmResults =
|
||||
GridLayoutManager(
|
||||
context,
|
||||
max((resources.configuration.screenWidthDp.toDouble() / resources.getInteger(R.integer.column_width_dp)).toInt(), 1)
|
||||
(resources.configuration.screenWidthDp / resources.getDimension(R.dimen.landscape_threshold)).toInt() + 1
|
||||
);
|
||||
return glmResults
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.time.OffsetDateTime
|
||||
import kotlin.math.max
|
||||
|
||||
abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : LinearLayout where TPager : IPager<TResult>, TViewHolder : RecyclerView.ViewHolder, TFragment : MainFragment {
|
||||
protected val _recyclerResults: RecyclerView;
|
||||
@@ -235,8 +234,7 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
|
||||
}
|
||||
|
||||
open fun updateSpanCount() {
|
||||
recyclerData.layoutManager.spanCount =
|
||||
max((resources.configuration.screenWidthDp.toDouble() / resources.getInteger(R.integer.column_width_dp)).toInt(), 1)
|
||||
recyclerData.layoutManager.spanCount = (resources.configuration.screenWidthDp / resources.getDimension(R.dimen.landscape_threshold)).toInt() + 1
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration?) {
|
||||
|
||||
+34
-180
@@ -1,16 +1,11 @@
|
||||
package com.futo.platformplayer.fragment.mainactivity.main
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.res.Configuration
|
||||
import android.database.ContentObserver
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.OrientationEventListener
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowInsets
|
||||
@@ -33,18 +28,13 @@ import com.futo.platformplayer.models.PlatformVideoWithTime
|
||||
import com.futo.platformplayer.models.UrlVideoWithTime
|
||||
import com.futo.platformplayer.states.StatePlayer
|
||||
import com.futo.platformplayer.views.containers.SingleViewTouchableMotionLayout
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.min
|
||||
|
||||
//region Fragment
|
||||
@UnstableApi
|
||||
class VideoDetailFragment() : MainFragment() {
|
||||
override val isMainView: Boolean = false;
|
||||
class VideoDetailFragment : MainFragment {
|
||||
override val isMainView : Boolean = false;
|
||||
override val hasBottomBar: Boolean = true;
|
||||
override val isOverlay: Boolean = true;
|
||||
override val isOverlay : Boolean = true;
|
||||
override val isHistory: Boolean = false;
|
||||
|
||||
private var _isActive: Boolean = false;
|
||||
@@ -86,9 +76,8 @@ class VideoDetailFragment() : MainFragment() {
|
||||
private var _loadUrlOnCreate: UrlVideoWithTime? = null;
|
||||
private var _leavingPiP = false;
|
||||
|
||||
private var _landscapeOrientationListener: LandscapeOrientationListener? = null
|
||||
private var _portraitOrientationListener: PortraitOrientationListener? = null
|
||||
private var _autoRotateObserver: AutoRotateObserver? = null
|
||||
//region Fragment
|
||||
constructor() : super()
|
||||
|
||||
fun nextVideo() {
|
||||
_viewDetail?.nextVideo(true, true, true);
|
||||
@@ -99,27 +88,23 @@ class VideoDetailFragment() : MainFragment() {
|
||||
}
|
||||
|
||||
private fun isSmallWindow(): Boolean {
|
||||
return resources.configuration.smallestScreenWidthDp < resources.getInteger(R.integer.column_width_dp) * 2
|
||||
}
|
||||
|
||||
private fun isAutoRotateEnabled(): Boolean {
|
||||
return android.provider.Settings.System.getInt(
|
||||
context?.contentResolver,
|
||||
android.provider.Settings.System.ACCELEROMETER_ROTATION, 0
|
||||
) == 1
|
||||
return min(
|
||||
resources.configuration.screenWidthDp,
|
||||
resources.configuration.screenHeightDp
|
||||
) < resources.getDimension(R.dimen.landscape_threshold)
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
|
||||
val isLandscapeVideo: Boolean = _viewDetail?.isLandscapeVideo() ?: false
|
||||
|
||||
val isSmallWindow = isSmallWindow()
|
||||
|
||||
if (
|
||||
isSmallWindow
|
||||
&& newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
&& !isFullscreen
|
||||
&& !isInPictureInPicture
|
||||
&& state == State.MAXIMIZED
|
||||
) {
|
||||
_viewDetail?.setFullscreen(true)
|
||||
@@ -156,63 +141,49 @@ class VideoDetailFragment() : MainFragment() {
|
||||
) {
|
||||
_viewDetail?.setFullscreen(true)
|
||||
}
|
||||
|
||||
updateOrientation()
|
||||
}
|
||||
|
||||
@SuppressLint("SourceLockedOrientationActivity")
|
||||
fun updateOrientation() {
|
||||
val a = activity ?: return
|
||||
val isFullScreenPortraitAllowed = Settings.instance.playback.fullscreenPortrait
|
||||
val isReversePortraitAllowed = Settings.instance.playback.reversePortrait
|
||||
val rotationLock = StatePlayer.instance.rotationLock
|
||||
val alwaysAllowReverseLandscapeAutoRotate = Settings.instance.playback.alwaysAllowReverseLandscapeAutoRotate
|
||||
|
||||
val isLandscapeVideo: Boolean = _viewDetail?.isLandscapeVideo() ?: true
|
||||
val isLandscapeVideo: Boolean = _viewDetail?.isLandscapeVideo() ?: false
|
||||
|
||||
val isSmallWindow = isSmallWindow()
|
||||
val autoRotateEnabled = isAutoRotateEnabled()
|
||||
|
||||
// For small windows if the device isn't landscape right now and full screen portrait isn't allowed then we should force landscape
|
||||
if (isSmallWindow && isFullscreen && !isFullScreenPortraitAllowed && resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT && !rotationLock && isLandscapeVideo) {
|
||||
if (alwaysAllowReverseLandscapeAutoRotate){
|
||||
a.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
||||
} else {
|
||||
a.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
|
||||
}
|
||||
if (autoRotateEnabled
|
||||
) {
|
||||
// start listening for the device to rotate to landscape
|
||||
// at which point we'll be able to set requestedOrientation to back to UNSPECIFIED
|
||||
_landscapeOrientationListener?.enableListener()
|
||||
}
|
||||
}
|
||||
// For small windows if always all reverse landscape then we'll lock the orientation to landscape when system auto-rotate is off to make sure that locking
|
||||
// and unlockiung in the player settings keep orientation in landscape
|
||||
else if (isSmallWindow && isFullscreen && !isFullScreenPortraitAllowed && alwaysAllowReverseLandscapeAutoRotate && !rotationLock && isLandscapeVideo && !autoRotateEnabled) {
|
||||
a.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
||||
a.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
|
||||
}
|
||||
// For small windows if the device isn't in a portrait orientation and we're in the maximized state then we should force portrait
|
||||
// only do this if auto-rotate is on portrait is forced when leaving full screen for autorotate off
|
||||
else if (isSmallWindow && !isMinimizingFromFullScreen && !isFullscreen && state == State.MAXIMIZED && resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
@SuppressLint("SourceLockedOrientationActivity")
|
||||
a.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
|
||||
if (autoRotateEnabled
|
||||
) {
|
||||
// start listening for the device to rotate to portrait
|
||||
// at which point we'll be able to set requestedOrientation to back to UNSPECIFIED
|
||||
_portraitOrientationListener?.enableListener()
|
||||
}
|
||||
} else if (rotationLock) {
|
||||
_portraitOrientationListener?.disableListener()
|
||||
_landscapeOrientationListener?.disableListener()
|
||||
a.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
|
||||
} else {
|
||||
_portraitOrientationListener?.disableListener()
|
||||
_landscapeOrientationListener?.disableListener()
|
||||
a.requestedOrientation = if (isReversePortraitAllowed) {
|
||||
ActivityInfo.SCREEN_ORIENTATION_FULL_USER
|
||||
} else {
|
||||
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
|
||||
when (Settings.instance.playback.autoRotate) {
|
||||
0 -> {
|
||||
a.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
|
||||
}
|
||||
|
||||
1 -> {
|
||||
a.requestedOrientation = if (isReversePortraitAllowed) {
|
||||
ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
|
||||
} else {
|
||||
ActivityInfo.SCREEN_ORIENTATION_SENSOR
|
||||
}
|
||||
}
|
||||
|
||||
2 -> {
|
||||
a.requestedOrientation = if (isReversePortraitAllowed) {
|
||||
ActivityInfo.SCREEN_ORIENTATION_FULL_USER
|
||||
} else {
|
||||
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -383,30 +354,6 @@ class VideoDetailFragment() : MainFragment() {
|
||||
StatePlayer.instance.onRotationLockChanged.subscribe(this) {
|
||||
updateOrientation()
|
||||
}
|
||||
|
||||
val delayBeforeRemoveRotationLock = 800L
|
||||
|
||||
_landscapeOrientationListener = LandscapeOrientationListener(requireContext())
|
||||
{
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
// delay to make sure that the system auto rotate updates
|
||||
delay(delayBeforeRemoveRotationLock)
|
||||
updateOrientation()
|
||||
}
|
||||
}
|
||||
_portraitOrientationListener = PortraitOrientationListener(requireContext())
|
||||
{
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
// delay to make sure that the system auto rotate updates
|
||||
delay(delayBeforeRemoveRotationLock)
|
||||
updateOrientation()
|
||||
}
|
||||
}
|
||||
_autoRotateObserver = AutoRotateObserver(requireContext(), Handler(Looper.getMainLooper())) {
|
||||
updateOrientation()
|
||||
}
|
||||
_autoRotateObserver?.startObserving()
|
||||
|
||||
return _view!!;
|
||||
}
|
||||
|
||||
@@ -508,10 +455,6 @@ class VideoDetailFragment() : MainFragment() {
|
||||
SettingsActivity.settingsActivityClosed.remove(this)
|
||||
StatePlayer.instance.onRotationLockChanged.remove(this)
|
||||
|
||||
_landscapeOrientationListener?.disableListener()
|
||||
_portraitOrientationListener?.disableListener()
|
||||
_autoRotateObserver?.stopObserving()
|
||||
|
||||
_viewDetail?.let {
|
||||
_viewDetail = null;
|
||||
it.onDestroy();
|
||||
@@ -583,11 +526,6 @@ class VideoDetailFragment() : MainFragment() {
|
||||
showSystemUI()
|
||||
}
|
||||
|
||||
// temporarily force the device to portrait if auto-rotate is disabled to prevent landscape when exiting full screen on a small device
|
||||
// @SuppressLint("SourceLockedOrientationActivity")
|
||||
// if (!isFullscreen && isSmallWindow() && !isAutoRotateEnabled() && !isMinimizingFromFullScreen) {
|
||||
// activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
|
||||
// }
|
||||
updateOrientation();
|
||||
_view?.allowMotion = !fullscreen;
|
||||
}
|
||||
@@ -609,88 +547,4 @@ class VideoDetailFragment() : MainFragment() {
|
||||
//region View
|
||||
//TODO: Determine if encapsulated would be readable enough
|
||||
//endregion
|
||||
}
|
||||
|
||||
class LandscapeOrientationListener(
|
||||
context: Context,
|
||||
private val onLandscapeDetected: () -> Unit
|
||||
) : OrientationEventListener(context) {
|
||||
|
||||
private var isListening = false
|
||||
|
||||
override fun onOrientationChanged(orientation: Int) {
|
||||
if (!isListening) return
|
||||
|
||||
if (orientation in 60..120 || orientation in 240..300) {
|
||||
onLandscapeDetected()
|
||||
disableListener()
|
||||
}
|
||||
}
|
||||
|
||||
fun enableListener() {
|
||||
if (!isListening) {
|
||||
isListening = true
|
||||
enable()
|
||||
}
|
||||
}
|
||||
|
||||
fun disableListener() {
|
||||
if (isListening) {
|
||||
isListening = false
|
||||
disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PortraitOrientationListener(
|
||||
context: Context,
|
||||
private val onPortraitDetected: () -> Unit
|
||||
) : OrientationEventListener(context) {
|
||||
|
||||
private var isListening = false
|
||||
|
||||
override fun onOrientationChanged(orientation: Int) {
|
||||
if (!isListening) return
|
||||
|
||||
if (orientation in 0..30 || orientation in 330..360 || orientation in 150..210) {
|
||||
onPortraitDetected()
|
||||
disableListener()
|
||||
}
|
||||
}
|
||||
|
||||
fun enableListener() {
|
||||
if (!isListening) {
|
||||
isListening = true
|
||||
enable()
|
||||
}
|
||||
}
|
||||
|
||||
fun disableListener() {
|
||||
if (isListening) {
|
||||
isListening = false
|
||||
disable()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AutoRotateObserver(context: Context, handler: Handler, private val onAutoRotateChanged: () -> Unit) : ContentObserver(handler) {
|
||||
private val contentResolver = context.contentResolver
|
||||
|
||||
override fun onChange(selfChange: Boolean) {
|
||||
super.onChange(selfChange)
|
||||
|
||||
onAutoRotateChanged()
|
||||
}
|
||||
|
||||
fun startObserving() {
|
||||
contentResolver.registerContentObserver(
|
||||
android.provider.Settings.System.getUriFor(android.provider.Settings.System.ACCELEROMETER_ROTATION),
|
||||
false,
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
fun stopObserving() {
|
||||
contentResolver.unregisterContentObserver(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-8
@@ -2384,13 +2384,8 @@ class VideoDetailView : ConstraintLayout {
|
||||
}
|
||||
|
||||
fun isLandscapeVideo(): Boolean? {
|
||||
var videoSourceWidth = _player.exoPlayer?.player?.videoSize?.width
|
||||
var videoSourceHeight = _player.exoPlayer?.player?.videoSize?.height
|
||||
|
||||
if (video?.video?.videoSources?.isNotEmpty() == true && (videoSourceWidth == null || videoSourceHeight == null || videoSourceWidth == 0 || videoSourceHeight == 0)) {
|
||||
videoSourceWidth = video?.video?.videoSources!![0].width
|
||||
videoSourceHeight = video?.video?.videoSources!![0].height
|
||||
}
|
||||
val videoSourceWidth = _player.exoPlayer?.player?.videoSize?.width
|
||||
val videoSourceHeight = _player.exoPlayer?.player?.videoSize?.height
|
||||
|
||||
return if (videoSourceWidth == null || videoSourceHeight == null || videoSourceWidth == 0 || videoSourceHeight == 0){
|
||||
null
|
||||
@@ -2592,6 +2587,7 @@ class VideoDetailView : ConstraintLayout {
|
||||
_overlayContainer.removeAllViews();
|
||||
_overlay_quality_selector?.hide();
|
||||
|
||||
_player.setFullScreen(true)
|
||||
_player.fillHeight(false)
|
||||
_layoutPlayerContainer.setPadding(0, 0, 0, 0);
|
||||
}
|
||||
@@ -2806,7 +2802,7 @@ class VideoDetailView : ConstraintLayout {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
if (fragment.state == VideoDetailFragment.State.MINIMIZED) {
|
||||
_player.fillHeight(true)
|
||||
} else if (!fragment.isFullscreen && !fragment.isInPictureInPicture) {
|
||||
} else if (!fragment.isFullscreen) {
|
||||
_player.fitHeight()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,25 +55,21 @@ class ServiceRecordAggregator {
|
||||
if (_cts != null) throw Exception("Already started.")
|
||||
|
||||
_cts = CoroutineScope(Dispatchers.Default).launch {
|
||||
try {
|
||||
while (isActive) {
|
||||
val now = Date()
|
||||
synchronized(_currentServices) {
|
||||
_cachedAddressRecords.forEach { it.value.removeAll { record -> now.after(record.expirationTime) } }
|
||||
_cachedTxtRecords.entries.removeIf { now.after(it.value.expirationTime) }
|
||||
_cachedSrvRecords.entries.removeIf { now.after(it.value.expirationTime) }
|
||||
_cachedPtrRecords.forEach { it.value.removeAll { record -> now.after(record.expirationTime) } }
|
||||
while (isActive) {
|
||||
val now = Date()
|
||||
synchronized(_currentServices) {
|
||||
_cachedAddressRecords.forEach { it.value.removeAll { record -> now.after(record.expirationTime) } }
|
||||
_cachedTxtRecords.entries.removeIf { now.after(it.value.expirationTime) }
|
||||
_cachedSrvRecords.entries.removeIf { now.after(it.value.expirationTime) }
|
||||
_cachedPtrRecords.forEach { it.value.removeAll { record -> now.after(record.expirationTime) } }
|
||||
|
||||
val newServices = getCurrentServices()
|
||||
_currentServices.clear()
|
||||
_currentServices.addAll(newServices)
|
||||
}
|
||||
|
||||
onServicesUpdated?.invoke(_currentServices.toList())
|
||||
delay(5000)
|
||||
val newServices = getCurrentServices()
|
||||
_currentServices.clear()
|
||||
_currentServices.addAll(newServices)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
Logger.e(TAG, "Unexpected failure in MDNS loop", e)
|
||||
|
||||
onServicesUpdated?.invoke(_currentServices.toList())
|
||||
delay(5000)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,7 +83,6 @@ class ServiceRecordAggregator {
|
||||
}
|
||||
|
||||
fun add(packet: DnsPacket) {
|
||||
val currentServices: List<DnsService>
|
||||
val dnsResourceRecords = packet.answers + packet.additionals + packet.authorities
|
||||
val txtRecords = dnsResourceRecords.filter { it.type == ResourceRecordType.TXT.value.toInt() }.map { it to it.getDataReader().readTXTRecord() }
|
||||
val aRecords = dnsResourceRecords.filter { it.type == ResourceRecordType.A.value.toInt() }.map { it to it.getDataReader().readARecord() }
|
||||
@@ -104,33 +99,35 @@ class ServiceRecordAggregator {
|
||||
aaaaRecords.forEach { builder.appendLine("AAAA ${it.first.name} ${it.first.type} ${it.first.clazz} TTL ${it.first.timeToLive}: ${it.second.address}") }
|
||||
Logger.i(TAG, "$builder")*/
|
||||
|
||||
val currentServices: MutableList<DnsService>
|
||||
ptrRecords.forEach { record ->
|
||||
val cachedPtrRecord = _cachedPtrRecords.getOrPut(record.first.name) { mutableListOf() }
|
||||
val newPtrRecord = CachedDnsPtrRecord(Date(System.currentTimeMillis() + record.first.timeToLive.toLong() * 1000L), record.second.domainName)
|
||||
cachedPtrRecord.replaceOrAdd(newPtrRecord) { it.target == record.second.domainName }
|
||||
}
|
||||
|
||||
aRecords.forEach { aRecord ->
|
||||
val cachedARecord = _cachedAddressRecords.getOrPut(aRecord.first.name) { mutableListOf() }
|
||||
val newARecord = CachedDnsAddressRecord(Date(System.currentTimeMillis() + aRecord.first.timeToLive.toLong() * 1000L), aRecord.second.address)
|
||||
cachedARecord.replaceOrAdd(newARecord) { it.address == newARecord.address }
|
||||
}
|
||||
|
||||
aaaaRecords.forEach { aaaaRecord ->
|
||||
val cachedAaaaRecord = _cachedAddressRecords.getOrPut(aaaaRecord.first.name) { mutableListOf() }
|
||||
val newAaaaRecord = CachedDnsAddressRecord(Date(System.currentTimeMillis() + aaaaRecord.first.timeToLive.toLong() * 1000L), aaaaRecord.second.address)
|
||||
cachedAaaaRecord.replaceOrAdd(newAaaaRecord) { it.address == newAaaaRecord.address }
|
||||
}
|
||||
|
||||
txtRecords.forEach { txtRecord ->
|
||||
_cachedTxtRecords[txtRecord.first.name] = CachedDnsTxtRecord(Date(System.currentTimeMillis() + txtRecord.first.timeToLive.toLong() * 1000L), txtRecord.second.texts)
|
||||
}
|
||||
|
||||
srvRecords.forEach { srvRecord ->
|
||||
_cachedSrvRecords[srvRecord.first.name] = CachedDnsSrvRecord(Date(System.currentTimeMillis() + srvRecord.first.timeToLive.toLong() * 1000L), srvRecord.second)
|
||||
}
|
||||
|
||||
//TODO: Maybe this can be debounced?
|
||||
synchronized(this._currentServices) {
|
||||
ptrRecords.forEach { record ->
|
||||
val cachedPtrRecord = _cachedPtrRecords.getOrPut(record.first.name) { mutableListOf() }
|
||||
val newPtrRecord = CachedDnsPtrRecord(Date(System.currentTimeMillis() + record.first.timeToLive.toLong() * 1000L), record.second.domainName)
|
||||
cachedPtrRecord.replaceOrAdd(newPtrRecord) { it.target == record.second.domainName }
|
||||
}
|
||||
|
||||
aRecords.forEach { aRecord ->
|
||||
val cachedARecord = _cachedAddressRecords.getOrPut(aRecord.first.name) { mutableListOf() }
|
||||
val newARecord = CachedDnsAddressRecord(Date(System.currentTimeMillis() + aRecord.first.timeToLive.toLong() * 1000L), aRecord.second.address)
|
||||
cachedARecord.replaceOrAdd(newARecord) { it.address == newARecord.address }
|
||||
}
|
||||
|
||||
aaaaRecords.forEach { aaaaRecord ->
|
||||
val cachedAaaaRecord = _cachedAddressRecords.getOrPut(aaaaRecord.first.name) { mutableListOf() }
|
||||
val newAaaaRecord = CachedDnsAddressRecord(Date(System.currentTimeMillis() + aaaaRecord.first.timeToLive.toLong() * 1000L), aaaaRecord.second.address)
|
||||
cachedAaaaRecord.replaceOrAdd(newAaaaRecord) { it.address == newAaaaRecord.address }
|
||||
}
|
||||
|
||||
txtRecords.forEach { txtRecord ->
|
||||
_cachedTxtRecords[txtRecord.first.name] = CachedDnsTxtRecord(Date(System.currentTimeMillis() + txtRecord.first.timeToLive.toLong() * 1000L), txtRecord.second.texts)
|
||||
}
|
||||
|
||||
srvRecords.forEach { srvRecord ->
|
||||
_cachedSrvRecords[srvRecord.first.name] = CachedDnsSrvRecord(Date(System.currentTimeMillis() + srvRecord.first.timeToLive.toLong() * 1000L), srvRecord.second)
|
||||
}
|
||||
|
||||
currentServices = getCurrentServices()
|
||||
this._currentServices.clear()
|
||||
this._currentServices.addAll(currentServices)
|
||||
|
||||
@@ -59,7 +59,7 @@ class PlatformLinkMovementMethod(private val _context: Context) : LinkMovementMe
|
||||
if (linkPressed && pressedLinks != null) {
|
||||
val dx = event.x - downX
|
||||
val dy = event.y - downY
|
||||
if (Math.abs(dx) <= touchSlop && Math.abs(dy) <= touchSlop && isTouchInside(widget, event)) {
|
||||
if (Math.abs(dx) <= touchSlop && Math.abs(dy) <= touchSlop) {
|
||||
runBlocking {
|
||||
for (link in pressedLinks!!) {
|
||||
Logger.i(TAG) { "Link clicked '${link.url}'." }
|
||||
@@ -101,7 +101,7 @@ class PlatformLinkMovementMethod(private val _context: Context) : LinkMovementMe
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return super.onTouchEvent(widget, buffer, event)
|
||||
}
|
||||
|
||||
private fun findLinksAtTouchPosition(widget: TextView, buffer: Spannable, event: MotionEvent): Array<URLSpan> {
|
||||
@@ -114,10 +114,6 @@ class PlatformLinkMovementMethod(private val _context: Context) : LinkMovementMe
|
||||
return buffer.getSpans(off, off, URLSpan::class.java)
|
||||
}
|
||||
|
||||
private fun isTouchInside(widget: TextView, event: MotionEvent): Boolean {
|
||||
return event.x >= 0 && event.x <= widget.width && event.y >= 0 && event.y <= widget.height
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "PlatformLinkMovementMethod"
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
package com.futo.platformplayer.receivers
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.view.KeyEvent
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
|
||||
|
||||
class MediaButtonReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
val keyEvent: KeyEvent? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
intent?.getParcelableExtra(Intent.EXTRA_KEY_EVENT, KeyEvent::class.java)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
(intent?.getParcelableExtra(Intent.EXTRA_KEY_EVENT))
|
||||
}
|
||||
|
||||
Logger.i(TAG, "Received media button intent, keyCode: " + keyEvent?.keyCode)
|
||||
if (keyEvent != null && keyEvent.action == KeyEvent.ACTION_DOWN) {
|
||||
when (keyEvent.keyCode) {
|
||||
KeyEvent.KEYCODE_MEDIA_PLAY -> MediaControlReceiver.onPlayReceived.emit()
|
||||
KeyEvent.KEYCODE_MEDIA_PAUSE -> MediaControlReceiver.onPauseReceived.emit()
|
||||
KeyEvent.KEYCODE_MEDIA_NEXT -> MediaControlReceiver.onNextReceived.emit()
|
||||
KeyEvent.KEYCODE_MEDIA_PREVIOUS -> MediaControlReceiver.onPreviousReceived.emit()
|
||||
KeyEvent.KEYCODE_MEDIA_STOP -> MediaControlReceiver.onCloseReceived.emit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = "MediaButtonReceiver"
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import android.support.v4.media.MediaMetadataCompat
|
||||
import android.support.v4.media.session.MediaSessionCompat
|
||||
import android.support.v4.media.session.PlaybackStateCompat
|
||||
import android.util.Log
|
||||
import android.view.KeyEvent
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.target.CustomTarget
|
||||
@@ -33,7 +32,6 @@ import com.futo.platformplayer.Settings
|
||||
import com.futo.platformplayer.activities.MainActivity
|
||||
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.receivers.MediaButtonReceiver
|
||||
import com.futo.platformplayer.receivers.MediaControlReceiver
|
||||
import com.futo.platformplayer.states.StatePlatform
|
||||
import com.futo.platformplayer.states.StatePlayer
|
||||
@@ -93,7 +91,6 @@ class MediaPlaybackService : Service() {
|
||||
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
fun setupNotificationRequirements() {
|
||||
_audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager;
|
||||
_notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager;
|
||||
@@ -104,7 +101,6 @@ class MediaPlaybackService : Service() {
|
||||
_notificationManager!!.createNotificationChannel(_notificationChannel!!);
|
||||
|
||||
_mediaSession = MediaSessionCompat(this, "PlayerState");
|
||||
_mediaSession?.isActive = true
|
||||
_mediaSession?.setPlaybackState(PlaybackStateCompat.Builder()
|
||||
.setState(PlaybackStateCompat.STATE_PLAYING, 0, 1f)
|
||||
.build());
|
||||
@@ -147,12 +143,6 @@ class MediaPlaybackService : Service() {
|
||||
MediaControlReceiver.onNextReceived.emit();
|
||||
}
|
||||
});
|
||||
_mediaSession?.setMediaButtonReceiver(PendingIntent.getBroadcast(
|
||||
this@MediaPlaybackService,
|
||||
0,
|
||||
Intent(Intent.ACTION_MEDIA_BUTTON).setClass(this@MediaPlaybackService, MediaButtonReceiver::class.java),
|
||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||
))
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
|
||||
@@ -160,6 +160,10 @@ class StateApp {
|
||||
private var _cacheDirectory: File? = null;
|
||||
private var _persistentDirectory: File? = null;
|
||||
|
||||
|
||||
//AutoRotate
|
||||
var systemAutoRotate: Boolean = false;
|
||||
|
||||
//Network
|
||||
private var _lastMeteredState: Boolean = false;
|
||||
private var _connectivityManager: ConnectivityManager? = null;
|
||||
@@ -197,6 +201,17 @@ class StateApp {
|
||||
return File(_persistentDirectory, name);
|
||||
}
|
||||
|
||||
fun getCurrentSystemAutoRotate(): Boolean {
|
||||
_context?.let {
|
||||
systemAutoRotate = android.provider.Settings.System.getInt(
|
||||
it.contentResolver,
|
||||
android.provider.Settings.System.ACCELEROMETER_ROTATION, 0
|
||||
) == 1;
|
||||
};
|
||||
return systemAutoRotate;
|
||||
}
|
||||
|
||||
|
||||
fun isCurrentMetered(): Boolean {
|
||||
ensureConnectivityManager();
|
||||
return _connectivityManager?.isActiveNetworkMetered ?: throw IllegalStateException("Connectivity manager not available");
|
||||
@@ -297,6 +312,9 @@ class StateApp {
|
||||
fun setGlobalContext(context: Context, coroutineScope: CoroutineScope? = null) {
|
||||
_context = context;
|
||||
_scope = coroutineScope
|
||||
|
||||
//System checks
|
||||
systemAutoRotate = getCurrentSystemAutoRotate();
|
||||
}
|
||||
|
||||
fun initializeFiles(force: Boolean = false) {
|
||||
|
||||
@@ -251,7 +251,7 @@ class StateDownloads {
|
||||
}
|
||||
else {
|
||||
Logger.i(TAG, "New watchlater video ${item.name}");
|
||||
download(VideoDownload(item, playlistDownload.targetPxCount, playlistDownload.targetBitrate, true)
|
||||
download(VideoDownload(item, playlistDownload.targetPxCount, playlistDownload.targetBitrate)
|
||||
.withGroup(VideoDownload.GROUP_WATCHLATER, VideoDownload.GROUP_WATCHLATER), false);
|
||||
hasNew = true;
|
||||
}
|
||||
@@ -296,7 +296,7 @@ class StateDownloads {
|
||||
}
|
||||
else {
|
||||
Logger.i(TAG, "New playlist video ${item.name}");
|
||||
download(VideoDownload(item, playlistDownload.targetPxCount, playlistDownload.targetBitrate, true)
|
||||
download(VideoDownload(item, playlistDownload.targetPxCount, playlistDownload.targetBitrate)
|
||||
.withGroup(VideoDownload.GROUP_PLAYLIST, playlist.id), false);
|
||||
hasNew = true;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ class NonScrollingTextView : androidx.appcompat.widget.AppCompatTextView {
|
||||
if (linkPressed && _lastTouchedLinks != null) {
|
||||
val dx = event.x - downX
|
||||
val dy = event.y - downY
|
||||
if (Math.abs(dx) <= touchSlop && Math.abs(dy) <= touchSlop && isTouchInside(event)) {
|
||||
if (Math.abs(dx) <= touchSlop && Math.abs(dy) <= touchSlop) {
|
||||
runBlocking {
|
||||
for (link in _lastTouchedLinks!!) {
|
||||
Logger.i(PlatformLinkMovementMethod.TAG) { "Link clicked '${link.url}'." }
|
||||
@@ -119,11 +119,7 @@ class NonScrollingTextView : androidx.appcompat.widget.AppCompatTextView {
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun isTouchInside(event: MotionEvent): Boolean {
|
||||
return event.x >= 0 && event.x <= width && event.y >= 0 && event.y <= height
|
||||
return super.onTouchEvent(event)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -592,6 +592,11 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
fun setFullScreen(fullScreen: Boolean) {
|
||||
// prevent fullscreen before the video has loaded to make sure we know whether it's a vertical or horizontal video
|
||||
if(exoPlayer?.player?.videoSize?.height ?: 0 == 0 && fullScreen){
|
||||
return
|
||||
}
|
||||
|
||||
updateRotateLock()
|
||||
|
||||
if (isFullScreen == fullScreen) {
|
||||
@@ -808,12 +813,17 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||
}
|
||||
|
||||
fun updateRotateLock() {
|
||||
_control_rotate_lock.visibility = View.VISIBLE;
|
||||
_control_rotate_lock_fullscreen.visibility = View.VISIBLE;
|
||||
|
||||
if(Settings.instance.playback.autoRotate == 0) {
|
||||
_control_rotate_lock.visibility = View.GONE;
|
||||
_control_rotate_lock_fullscreen.visibility = View.GONE;
|
||||
}
|
||||
else {
|
||||
_control_rotate_lock.visibility = View.VISIBLE;
|
||||
_control_rotate_lock_fullscreen.visibility = View.VISIBLE;
|
||||
}
|
||||
if(StatePlayer.instance.rotationLock) {
|
||||
_control_rotate_lock_fullscreen.setImageResource(R.drawable.ic_screen_lock_rotation_active);
|
||||
_control_rotate_lock.setImageResource(R.drawable.ic_screen_lock_rotation_active);
|
||||
_control_rotate_lock_fullscreen.setImageResource(R.drawable.ic_screen_rotation);
|
||||
_control_rotate_lock.setImageResource(R.drawable.ic_screen_rotation);
|
||||
}
|
||||
else {
|
||||
_control_rotate_lock_fullscreen.setImageResource(R.drawable.ic_screen_lock_rotation);
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillColor="@color/colorPrimary"
|
||||
android:pathData="M35.25,23.3 L36.4,22.2 38.6,24.45Q39.5,25.3 39.5,26.55Q39.5,27.8 38.6,28.65L32.5,34.75Q31.6,35.6 30.375,35.6Q29.15,35.6 28.3,34.75L13.8,20.25Q12.95,19.4 12.95,18.15Q12.95,16.9 13.8,16.1L19.95,9.95Q20.8,9.1 22.05,9.1Q23.3,9.1 24.1,9.95L26.45,12.25L25.3,13.35L22.9,10.95Q22.55,10.6 22.025,10.6Q21.5,10.6 21.15,10.95L14.85,17.25Q14.5,17.6 14.5,18.15Q14.5,18.7 14.85,19.05L29.5,33.75Q29.85,34.1 30.4,34.1Q30.95,34.1 31.25,33.75L37.6,27.4Q37.95,27.05 37.95,26.525Q37.95,26 37.6,25.65ZM26.15,44.45Q21.6,44.45 17.6,42.725Q13.6,41 10.625,38.025Q7.65,35.05 5.925,31.025Q4.2,27 4.2,22.5H5.75Q5.75,26.65 7.325,30.35Q8.9,34.05 11.65,36.825Q14.4,39.6 18.125,41.2Q21.85,42.8 26,42.85L18.55,35.35L19.65,34.25L29.5,44.1Q28.6,44.25 27.8,44.35Q27,44.45 26.15,44.45ZM32.4,18Q31.7,18 31.1,17.4Q30.5,16.8 30.5,16.1V10.85Q30.5,10.15 31.1,9.525Q31.7,8.9 32.4,8.9H32.55V6.85Q32.55,5.4 33.575,4.4Q34.6,3.4 36.05,3.4Q37.55,3.4 38.55,4.4Q39.55,5.4 39.55,6.85V8.9H39.75Q40.45,8.9 41,9.525Q41.55,10.15 41.55,10.85V16.1Q41.55,16.8 40.975,17.4Q40.4,18 39.65,18ZM34.05,8.9H38.05V6.85Q38.05,6 37.475,5.425Q36.9,4.85 36.05,4.85Q35.2,4.85 34.625,5.425Q34.05,6 34.05,6.85ZM26.25,22.35Q26.25,22.35 26.25,22.35Q26.25,22.35 26.25,22.35Q26.25,22.35 26.25,22.35Q26.25,22.35 26.25,22.35Q26.25,22.35 26.25,22.35Q26.25,22.35 26.25,22.35Q26.25,22.35 26.25,22.35Q26.25,22.35 26.25,22.35Q26.25,22.35 26.25,22.35Q26.25,22.35 26.25,22.35Z"/>
|
||||
</vector>
|
||||
@@ -233,6 +233,8 @@
|
||||
<string name="announcement">إعلان</string>
|
||||
<string name="attempt_to_utilize_byte_ranges">محاولة استخدام مدى البايت</string>
|
||||
<string name="auto_update">تحديث تلقائي</string>
|
||||
<string name="auto_rotate">تدوير تلقائي</string>
|
||||
<string name="auto_rotate_dead_zone">منطقة ميتة للتدوير التلقائي</string>
|
||||
<string name="automatic_backup">نسخ احتياطي تلقائي</string>
|
||||
<string name="background_behavior">سلوك الخلفية</string>
|
||||
<string name="background_update">تحديث الخلفية</string>
|
||||
@@ -537,6 +539,7 @@
|
||||
<string name="not_yet_available_retrying_in_time_s">لم يصبح متوفراً بعد، إعادة المحاولة في {time}s</string>
|
||||
<string name="failed_to_retry_for_live_stream">فشل في إعادة المحاولة للبث المباشر</string>
|
||||
<string name="this_app_is_in_development_please_submit_bug_reports_and_understand_that_many_features_are_incomplete">هذا التطبيق قيد التطوير. يرجى إرسال تقارير الأخطاء وفهم أن العديد من الميزات غير مكتملة.</string>
|
||||
<string name="please_use_at_least_3_characters">يرجى استخدام 3 أحرف على الأقل</string>
|
||||
<string name="are_you_sure_you_want_to_delete_this_video">هل أنت متأكد من أنك ترغب في حذف هذا الفيديو؟</string>
|
||||
<string name="tap_to_open">انقر للفتح</string>
|
||||
<string name="watching">يشاهد</string>
|
||||
@@ -658,6 +661,11 @@
|
||||
<item>عند التشغيل</item>
|
||||
<item>أبداً</item>
|
||||
</string-array>
|
||||
<string-array name="system_enabled_disabled_array">
|
||||
<item>معطل</item>
|
||||
<item>مفعل</item>
|
||||
<item>كما في النظام</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_disabled_array">
|
||||
<item>معطل</item>
|
||||
<item>مفعل</item>
|
||||
|
||||
@@ -243,6 +243,8 @@
|
||||
<string name="announcement">Ankündigung</string>
|
||||
<string name="attempt_to_utilize_byte_ranges">Versuch, Byte-Bereiche zu nutzen</string>
|
||||
<string name="auto_update">Automatische Aktualisierung</string>
|
||||
<string name="auto_rotate">Automatische Drehung</string>
|
||||
<string name="auto_rotate_dead_zone">Toter Winkel für automatische Drehung</string>
|
||||
<string name="automatic_backup">Automatisches Backup</string>
|
||||
<string name="background_behavior">Hintergrundverhalten</string>
|
||||
<string name="background_update">Hintergrundaktualisierung</string>
|
||||
@@ -540,6 +542,7 @@
|
||||
<string name="not_yet_available_retrying_in_time_s">Noch nicht verfügbar, erneuter Versuch in {time}s</string>
|
||||
<string name="failed_to_retry_for_live_stream">Fehler beim erneuten Versuch für den Live-Stream</string>
|
||||
<string name="this_app_is_in_development_please_submit_bug_reports_and_understand_that_many_features_are_incomplete">Diese App befindet sich in der Entwicklung. Bitte senden Sie Fehlerberichte und verstehen Sie, dass viele Funktionen unvollständig sind.</string>
|
||||
<string name="please_use_at_least_3_characters">Bitte verwenden Sie mindestens 3 Zeichen</string>
|
||||
<string name="are_you_sure_you_want_to_delete_this_video">Sind Sie sicher, dass Sie dieses Video löschen möchten?</string>
|
||||
<string name="tap_to_open">Tippen Sie zum Öffnen</string>
|
||||
<string name="watching">anschauen</string>
|
||||
@@ -658,6 +661,11 @@
|
||||
<item>Beim Start</item>
|
||||
<item>Nie</item>
|
||||
</string-array>
|
||||
<string-array name="system_enabled_disabled_array">
|
||||
<item>Deaktiviert</item>
|
||||
<item>Aktiviert</item>
|
||||
<item>Gleich wie System</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_disabled_array">
|
||||
<item>Deaktiviert</item>
|
||||
<item>Aktiviert</item>
|
||||
|
||||
@@ -217,6 +217,8 @@
|
||||
<string name="announcement">Anuncio</string>
|
||||
<string name="attempt_to_utilize_byte_ranges">Intentar utilizar rangos de bytes</string>
|
||||
<string name="auto_update">Actualización automática</string>
|
||||
<string name="auto_rotate">Auto-rotar</string>
|
||||
<string name="auto_rotate_dead_zone">Zona muerta de auto-rotación</string>
|
||||
<string name="automatic_backup">Copia de seguridad automática</string>
|
||||
<string name="background_behavior">Comportamiento en segundo plano</string>
|
||||
<string name="background_update">Actualización en segundo plano</string>
|
||||
@@ -521,6 +523,7 @@
|
||||
<string name="not_yet_available_retrying_in_time_s">Todavía no está disponible, reintento en {time}s</string>
|
||||
<string name="failed_to_retry_for_live_stream">Error al reintentar la transmisión en vivo</string>
|
||||
<string name="this_app_is_in_development_please_submit_bug_reports_and_understand_that_many_features_are_incomplete">Esta aplicación está en desarrollo. Por favor, envía informes de errores y comprende que muchas características están incompletas.</string>
|
||||
<string name="please_use_at_least_3_characters">Por favor, usa al menos 3 caracteres</string>
|
||||
<string name="are_you_sure_you_want_to_delete_this_video">¿Estás seguro de que deseas eliminar este video?</string>
|
||||
<string name="tap_to_open">Toca para abrir</string>
|
||||
<string name="watching">viendo</string>
|
||||
@@ -668,6 +671,17 @@
|
||||
<item>Al Iniciar</item>
|
||||
<item>Nunca</item>
|
||||
</string-array>
|
||||
<string-array name="system_enabled_disabled_array">
|
||||
<item>Desactivado</item>
|
||||
<item>Activado</item>
|
||||
<item>Mismo que el Sistema</item>
|
||||
</string-array>
|
||||
<string-array name="auto_rotate_dead_zone">
|
||||
<item>0</item>
|
||||
<item>5</item>
|
||||
<item>10</item>
|
||||
<item>20</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_disabled_array">
|
||||
<item>Desactivado</item>
|
||||
<item>Activado</item>
|
||||
|
||||
@@ -256,6 +256,8 @@
|
||||
<string name="announcement">Annonce</string>
|
||||
<string name="attempt_to_utilize_byte_ranges">Tentative d\'utilisation de plages d\'octets</string>
|
||||
<string name="auto_update">Mise à jour automatique</string>
|
||||
<string name="auto_rotate">Rotation automatique</string>
|
||||
<string name="auto_rotate_dead_zone">Zone morte de rotation automatique</string>
|
||||
<string name="automatic_backup">Sauvegarde automatique</string>
|
||||
<string name="background_behavior">Comportement en arrière-plan</string>
|
||||
<string name="background_update">Mise à jour en arrière-plan</string>
|
||||
@@ -560,6 +562,7 @@
|
||||
<string name="not_yet_available_retrying_in_time_s">Pas encore disponible, réessai dans {time}s</string>
|
||||
<string name="failed_to_retry_for_live_stream">Échec de la tentative de réessai pour la diffusion en direct</string>
|
||||
<string name="this_app_is_in_development_please_submit_bug_reports_and_understand_that_many_features_are_incomplete">Cette application est en développement. Veuillez soumettre des rapports de bug et comprendre que de nombreuses fonctionnalités ne sont pas encore complètes.</string>
|
||||
<string name="please_use_at_least_3_characters">Veuillez utiliser au moins 3 caractères</string>
|
||||
<string name="are_you_sure_you_want_to_delete_this_video">Êtes-vous sûr de vouloir supprimer cette vidéo ?</string>
|
||||
<string name="tap_to_open">Appuyez pour ouvrir</string>
|
||||
<string name="watching">en train de regarder</string>
|
||||
@@ -658,6 +661,11 @@
|
||||
<item>Au démarrage</item>
|
||||
<item>Jamais</item>
|
||||
</string-array>
|
||||
<string-array name="system_enabled_disabled_array">
|
||||
<item>Désactivé</item>
|
||||
<item>Activé</item>
|
||||
<item>Même que le système</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_disabled_array">
|
||||
<item>Désactivé</item>
|
||||
<item>Activé</item>
|
||||
|
||||
@@ -220,6 +220,8 @@
|
||||
<string name="announcement">お知らせ</string>
|
||||
<string name="attempt_to_utilize_byte_ranges">バイト範囲を使用する試み</string>
|
||||
<string name="auto_update">自動更新</string>
|
||||
<string name="auto_rotate">自動回転</string>
|
||||
<string name="auto_rotate_dead_zone">自動回転デッドゾーン</string>
|
||||
<string name="automatic_backup">自動バックアップ</string>
|
||||
<string name="background_behavior">バックグラウンドの動作</string>
|
||||
<string name="background_update">バックグラウンド更新</string>
|
||||
@@ -522,6 +524,7 @@
|
||||
<string name="not_yet_available_retrying_in_time_s">{time}秒後に再試行、まだ利用できません</string>
|
||||
<string name="failed_to_retry_for_live_stream">ライブストリームの再試行に失敗しました</string>
|
||||
<string name="this_app_is_in_development_please_submit_bug_reports_and_understand_that_many_features_are_incomplete">このアプリは開発中です。バグレポートを提出し、多くの機能が未完成であることを理解してください。</string>
|
||||
<string name="please_use_at_least_3_characters">少なくとも3文字を使用してください</string>
|
||||
<string name="are_you_sure_you_want_to_delete_this_video">このビデオを削除してもよろしいですか?</string>
|
||||
<string name="tap_to_open">タップして開く</string>
|
||||
<string name="watching">視聴中</string>
|
||||
@@ -658,6 +661,11 @@
|
||||
<item>起動時</item>
|
||||
<item>なし</item>
|
||||
</string-array>
|
||||
<string-array name="system_enabled_disabled_array">
|
||||
<item>無効</item>
|
||||
<item>有効</item>
|
||||
<item>システムと同じ</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_disabled_array">
|
||||
<item>無効</item>
|
||||
<item>有効</item>
|
||||
|
||||
@@ -255,6 +255,8 @@
|
||||
<string name="announcement">공고</string>
|
||||
<string name="attempt_to_utilize_byte_ranges">바이트 범위 사용 시도</string>
|
||||
<string name="auto_update">자동 업데이트</string>
|
||||
<string name="auto_rotate">자동 회전</string>
|
||||
<string name="auto_rotate_dead_zone">자동 회전 데드 존</string>
|
||||
<string name="automatic_backup">자동 백업</string>
|
||||
<string name="background_behavior">백그라운드 동작</string>
|
||||
<string name="background_update">백그라운드 업데이트</string>
|
||||
@@ -559,6 +561,7 @@
|
||||
<string name="not_yet_available_retrying_in_time_s">아직 사용할 수 없습니다, {time}초 후에 다시 시도합니다</string>
|
||||
<string name="failed_to_retry_for_live_stream">라이브 스트림을 다시 시도하지 못했습니다</string>
|
||||
<string name="this_app_is_in_development_please_submit_bug_reports_and_understand_that_many_features_are_incomplete">이 앱은 개발 중입니다. 버그 보고를 제출해 주시고, 많은 기능이 미완성임을 이해해 주세요.</string>
|
||||
<string name="please_use_at_least_3_characters">최소 3자 이상 사용해 주세요</string>
|
||||
<string name="are_you_sure_you_want_to_delete_this_video">이 비디오를 삭제하시겠습니까?</string>
|
||||
<string name="tap_to_open">열려면 탭하세요</string>
|
||||
<string name="watching">시청 중</string>
|
||||
@@ -658,6 +661,11 @@
|
||||
<item>시작할 때</item>
|
||||
<item>안 함</item>
|
||||
</string-array>
|
||||
<string-array name="system_enabled_disabled_array">
|
||||
<item>비활성화</item>
|
||||
<item>활성화</item>
|
||||
<item>시스템과 동일</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_disabled_array">
|
||||
<item>비활성화</item>
|
||||
<item>활성화</item>
|
||||
|
||||
@@ -256,6 +256,8 @@
|
||||
<string name="announcement">Anúncio</string>
|
||||
<string name="attempt_to_utilize_byte_ranges">Tentar utilizar intervalos de bytes</string>
|
||||
<string name="auto_update">Atualização Automática</string>
|
||||
<string name="auto_rotate">Rotação Automática</string>
|
||||
<string name="auto_rotate_dead_zone">Zona Morta de Rotação Automática</string>
|
||||
<string name="automatic_backup">Backup Automático</string>
|
||||
<string name="background_behavior">Comportamento em Segundo Plano</string>
|
||||
<string name="background_update">Atualização em Segundo Plano</string>
|
||||
@@ -555,6 +557,7 @@
|
||||
<string name="not_yet_available_retrying_in_time_s">Ainda não disponível, tentando novamente em {time}s</string>
|
||||
<string name="failed_to_retry_for_live_stream">Falha ao tentar novamente para transmissão ao vivo</string>
|
||||
<string name="this_app_is_in_development_please_submit_bug_reports_and_understand_that_many_features_are_incomplete">Este aplicativo está em desenvolvimento. Envie relatórios de erros e entenda que muitos recursos estão incompletos.</string>
|
||||
<string name="please_use_at_least_3_characters">Use pelo menos 3 caracteres</string>
|
||||
<string name="are_you_sure_you_want_to_delete_this_video">Tem certeza de que deseja excluir este vídeo?</string>
|
||||
<string name="tap_to_open">Toque para abrir</string>
|
||||
<string name="watching">Assistindo</string>
|
||||
@@ -658,6 +661,11 @@
|
||||
<item>Ao Iniciar</item>
|
||||
<item>Nunca</item>
|
||||
</string-array>
|
||||
<string-array name="system_enabled_disabled_array">
|
||||
<item>Desativado</item>
|
||||
<item>Ativado</item>
|
||||
<item>Como no Sistema</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_disabled_array">
|
||||
<item>Desativado</item>
|
||||
<item>Ativado</item>
|
||||
|
||||
@@ -252,6 +252,8 @@
|
||||
<string name="announcement">Объявление</string>
|
||||
<string name="attempt_to_utilize_byte_ranges">Попытка использовать диапазоны байт</string>
|
||||
<string name="auto_update">Автообновление</string>
|
||||
<string name="auto_rotate">Автоповорот</string>
|
||||
<string name="auto_rotate_dead_zone">Мертвая зона автоповорота</string>
|
||||
<string name="automatic_backup">Автоматическое резервное копирование</string>
|
||||
<string name="background_behavior">Поведение в фоновом режиме</string>
|
||||
<string name="background_update">Фоновое обновление</string>
|
||||
@@ -556,6 +558,7 @@
|
||||
<string name="not_yet_available_retrying_in_time_s">Ещё недоступно, повторная попытка через {time}с</string>
|
||||
<string name="failed_to_retry_for_live_stream">Не удалось повторить попытку для прямого эфира</string>
|
||||
<string name="this_app_is_in_development_please_submit_bug_reports_and_understand_that_many_features_are_incomplete">Это приложение находится в стадии разработки. Пожалуйста, отправляйте сообщения об ошибках и поймите, что многие функции незавершены.</string>
|
||||
<string name="please_use_at_least_3_characters">Пожалуйста, используйте хотя бы 3 символа</string>
|
||||
<string name="are_you_sure_you_want_to_delete_this_video">Вы уверены, что хотите удалить это видео?</string>
|
||||
<string name="tap_to_open">Нажмите, чтобы открыть</string>
|
||||
<string name="watching">Смотрят</string>
|
||||
@@ -658,6 +661,11 @@
|
||||
<item>При запуске</item>
|
||||
<item>Никогда</item>
|
||||
</string-array>
|
||||
<string-array name="system_enabled_disabled_array">
|
||||
<item>Отключено</item>
|
||||
<item>Включено</item>
|
||||
<item>Как в системе</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_disabled_array">
|
||||
<item>Отключено</item>
|
||||
<item>Включено</item>
|
||||
|
||||
@@ -256,6 +256,8 @@
|
||||
<string name="announcement">公告</string>
|
||||
<string name="attempt_to_utilize_byte_ranges">尝试使用字节范围</string>
|
||||
<string name="auto_update">自动更新</string>
|
||||
<string name="auto_rotate">自动旋转</string>
|
||||
<string name="auto_rotate_dead_zone">自动旋转死区</string>
|
||||
<string name="automatic_backup">自动备份</string>
|
||||
<string name="background_behavior">后台行为</string>
|
||||
<string name="background_update">后台更新</string>
|
||||
@@ -560,6 +562,7 @@
|
||||
<string name="not_yet_available_retrying_in_time_s">尚未可用,将在{time}s后重试</string>
|
||||
<string name="failed_to_retry_for_live_stream">无法重新尝试直播流</string>
|
||||
<string name="this_app_is_in_development_please_submit_bug_reports_and_understand_that_many_features_are_incomplete">此应用处于开发中。请提交错误报告,并理解许多功能尚未完成。</string>
|
||||
<string name="please_use_at_least_3_characters">请至少使用3个字符</string>
|
||||
<string name="are_you_sure_you_want_to_delete_this_video">您确定要删除此视频吗?</string>
|
||||
<string name="tap_to_open">点击打开</string>
|
||||
<string name="watching">正在观看</string>
|
||||
@@ -658,6 +661,11 @@
|
||||
<item>启动时</item>
|
||||
<item>从不</item>
|
||||
</string-array>
|
||||
<string-array name="system_enabled_disabled_array">
|
||||
<item>已禁用</item>
|
||||
<item>已启用</item>
|
||||
<item>与系统相同</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_disabled_array">
|
||||
<item>已禁用</item>
|
||||
<item>已启用</item>
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
<resources>
|
||||
<dimen name="minimized_player_max_width">500dp</dimen>
|
||||
<dimen name="app_bar_height">200dp</dimen>
|
||||
<integer name="column_width_dp">400</integer>
|
||||
<dimen name="landscape_threshold">300dp</dimen>
|
||||
</resources>
|
||||
|
||||
@@ -287,10 +287,10 @@
|
||||
<string name="planned_content_notifications_description">Schedules discovered planned content as notifications, resulting in more accurate notifications for this content.</string>
|
||||
<string name="attempt_to_utilize_byte_ranges">Attempt to utilize byte ranges</string>
|
||||
<string name="auto_update">Auto Update</string>
|
||||
<string name="always_allow_reverse_landscape_auto_rotate">Always allow reverse landscape auto-rotate</string>
|
||||
<string name="always_allow_reverse_landscape_auto_rotate_description">There will always be auto-rotation between the two landscape orientations in full-screen mode, even when you disable auto-rotate in system settings.</string>
|
||||
<string name="auto_rotate">Auto-Rotate</string>
|
||||
<string name="simplify_sources">Simplify sources</string>
|
||||
<string name="simplify_sources_description">Deduplicate sources by resolution so that only more relevant sources are visible.</string>
|
||||
<string name="auto_rotate_dead_zone">Auto-Rotate Dead Zone</string>
|
||||
<string name="automatic_backup">Automatic Backup</string>
|
||||
<string name="background_behavior">Background Behavior</string>
|
||||
<string name="background_update">Background Update</string>
|
||||
@@ -928,6 +928,17 @@
|
||||
<item>On Startup</item>
|
||||
<item>Never</item>
|
||||
</string-array>
|
||||
<string-array name="system_enabled_disabled_array">
|
||||
<item>Disabled</item>
|
||||
<item>Enabled</item>
|
||||
<item>Same as System</item>
|
||||
</string-array>
|
||||
<string-array name="auto_rotate_dead_zone" translatable="false">
|
||||
<item>0</item>
|
||||
<item>5</item>
|
||||
<item>10</item>
|
||||
<item>20</item>
|
||||
</string-array>
|
||||
<string-array name="enabled_disabled_array">
|
||||
<item>Disabled</item>
|
||||
<item>Enabled</item>
|
||||
|
||||
Submodule app/src/stable/assets/sources/apple-podcast deleted from f79c7141bc
Submodule app/src/stable/assets/sources/bilibili updated: 764316618b...258c71e4f5
Submodule app/src/stable/assets/sources/odysee updated: 5186dcd6c2...8ddb2e2f15
Submodule app/src/stable/assets/sources/soundcloud updated: 90bceac198...9a10cb8e78
Submodule app/src/stable/assets/sources/youtube updated: 1be5025b47...59d694b619
@@ -12,8 +12,7 @@
|
||||
"cf8ea74d-ad9b-489e-a083-539b6aa8648c": "sources/bilibili/build/BiliBiliConfig.json",
|
||||
"4e365633-6d3f-4267-8941-fdc36631d813": "sources/spotify/build/SpotifyConfig.json",
|
||||
"9c87e8db-e75d-48f4-afe5-2d203d4b95c5": "sources/dailymotion/build/DailymotionConfig.json",
|
||||
"e8b1ad5f-0c6d-497d-a5fa-0a785a16d902": "sources/bitchute/BitchuteConfig.json",
|
||||
"89ae4889-0420-4d16-ad6c-19c776b28f99": "sources/apple-podcasts/ApplePodcastsConfig.json"
|
||||
"e8b1ad5f-0c6d-497d-a5fa-0a785a16d902": "sources/bitchute/BitchuteConfig.json"
|
||||
},
|
||||
"SOURCES_EMBEDDED_DEFAULT": [
|
||||
"35ae969a-a7db-11ed-afa1-0242ac120002"
|
||||
|
||||
Submodule app/src/unstable/assets/sources/apple-podcasts deleted from f79c7141bc
Submodule app/src/unstable/assets/sources/bilibili updated: 764316618b...258c71e4f5
Submodule app/src/unstable/assets/sources/odysee updated: 5186dcd6c2...8ddb2e2f15
Submodule app/src/unstable/assets/sources/soundcloud updated: 90bceac198...9a10cb8e78
Submodule app/src/unstable/assets/sources/youtube updated: 1be5025b47...59d694b619
@@ -12,8 +12,7 @@
|
||||
"cf8ea74d-ad9b-489e-a083-539b6aa8648c": "sources/bilibili/build/BiliBiliConfig.json",
|
||||
"4e365633-6d3f-4267-8941-fdc36631d813": "sources/spotify/build/SpotifyConfig.json",
|
||||
"9c87e8db-e75d-48f4-afe5-2d203d4b95c5": "sources/dailymotion/build/DailymotionConfig.json",
|
||||
"e8b1ad5f-0c6d-497d-a5fa-0a785a16d902": "sources/bitchute/BitchuteConfig.json",
|
||||
"89ae4889-0420-4d16-ad6c-19c776b28f99": "sources/apple-podcasts/ApplePodcastsConfig.json"
|
||||
"e8b1ad5f-0c6d-497d-a5fa-0a785a16d902": "sources/bitchute/BitchuteConfig.json"
|
||||
},
|
||||
"SOURCES_EMBEDDED_DEFAULT": [
|
||||
"35ae969a-a7db-11ed-afa1-0242ac120002"
|
||||
|
||||
+1
-1
Submodule dep/futopay updated: 21afe43dff...c3f532c660
Reference in New Issue
Block a user