mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-08-04 23:30:52 +00:00
Merge branch 'simple-motion-layout' into 'master'
update MotionLayout code See merge request videostreaming/grayjay!67
This commit is contained in:
commit
22e71c7edc
37 changed files with 494 additions and 703 deletions
|
@ -162,7 +162,7 @@ dependencies {
|
||||||
implementation 'androidx.core:core-ktx:1.12.0'
|
implementation 'androidx.core:core-ktx:1.12.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
implementation 'com.google.android.material:material:1.11.0'
|
implementation 'com.google.android.material:material:1.11.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
|
||||||
|
|
||||||
//Images
|
//Images
|
||||||
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
|
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'
|
||||||
|
|
|
@ -339,14 +339,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
_fragVideoDetail.onMinimize.subscribe {
|
_fragVideoDetail.onMinimize.subscribe {
|
||||||
updateSegmentPaddings();
|
updateSegmentPaddings();
|
||||||
};
|
};
|
||||||
_fragVideoDetail.onTransitioning.subscribe {
|
|
||||||
if (it || _fragVideoDetail.state != VideoDetailFragment.State.MINIMIZED)
|
|
||||||
_fragContainerOverlay.elevation =
|
|
||||||
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 15f, resources.displayMetrics);
|
|
||||||
else
|
|
||||||
_fragContainerOverlay.elevation =
|
|
||||||
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics);
|
|
||||||
}
|
|
||||||
|
|
||||||
_fragVideoDetail.onCloseEvent.subscribe {
|
_fragVideoDetail.onCloseEvent.subscribe {
|
||||||
_fragMainHome.setPreviewsEnabled(true);
|
_fragMainHome.setPreviewsEnabled(true);
|
||||||
|
@ -1076,8 +1068,8 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
if (_fragContainerVideoDetail.visibility != View.VISIBLE)
|
if (_fragContainerVideoDetail.visibility != View.VISIBLE)
|
||||||
_fragContainerVideoDetail.visibility = View.VISIBLE;
|
_fragContainerVideoDetail.visibility = View.VISIBLE;
|
||||||
when (segment.state) {
|
when (segment.state) {
|
||||||
VideoDetailFragment.State.MINIMIZED -> segment.maximizeVideoDetail()
|
VideoDetailFragment.State.MINIMIZED -> segment.maximizeVideoDetail(false)
|
||||||
VideoDetailFragment.State.CLOSED -> segment.maximizeVideoDetail()
|
VideoDetailFragment.State.CLOSED -> segment.maximizeVideoDetail(false)
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
segment.onShown(parameter, isBack);
|
segment.onShown(parameter, isBack);
|
||||||
|
@ -1200,7 +1192,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun updateSegmentPaddings() {
|
private fun updateSegmentPaddings() {
|
||||||
var paddingBottom = 0f;
|
var paddingBottom = 0f;
|
||||||
if (fragCurrent.hasBottomBar)
|
if (fragCurrent.hasBottomBar)
|
||||||
|
@ -1211,9 +1202,6 @@ class MainActivity : AppCompatActivity, IWithResultLauncher {
|
||||||
.toInt()
|
.toInt()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (_fragVideoDetail.state == VideoDetailFragment.State.MINIMIZED)
|
|
||||||
paddingBottom += HEIGHT_VIDEO_MINIMIZED_DP;
|
|
||||||
|
|
||||||
_fragContainerMain.setPadding(
|
_fragContainerMain.setPadding(
|
||||||
0, 0, 0, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, paddingBottom, resources.displayMetrics)
|
0, 0, 0, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, paddingBottom, resources.displayMetrics)
|
||||||
.toInt()
|
.toInt()
|
||||||
|
|
|
@ -213,7 +213,7 @@ class ChannelFragment : MainFragment() {
|
||||||
when (v) {
|
when (v) {
|
||||||
is IPlatformVideo -> {
|
is IPlatformVideo -> {
|
||||||
StatePlayer.instance.clearQueue()
|
StatePlayer.instance.clearQueue()
|
||||||
fragment.navigate<VideoDetailFragment>(v).maximizeVideoDetail()
|
fragment.navigate<VideoDetailFragment>(v).maximizeVideoDetail(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
is IPlatformPlaylist -> {
|
is IPlatformPlaylist -> {
|
||||||
|
@ -249,7 +249,7 @@ class ChannelFragment : MainFragment() {
|
||||||
when (contentType) {
|
when (contentType) {
|
||||||
ContentType.MEDIA -> {
|
ContentType.MEDIA -> {
|
||||||
StatePlayer.instance.clearQueue()
|
StatePlayer.instance.clearQueue()
|
||||||
fragment.navigate<VideoDetailFragment>(url).maximizeVideoDetail()
|
fragment.navigate<VideoDetailFragment>(url).maximizeVideoDetail(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentType.URL -> fragment.navigate<BrowserFragment>(url)
|
ContentType.URL -> fragment.navigate<BrowserFragment>(url)
|
||||||
|
|
|
@ -188,9 +188,9 @@ abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent
|
||||||
StatePlayer.instance.insertToQueue(content, true);
|
StatePlayer.instance.insertToQueue(content, true);
|
||||||
} else {
|
} else {
|
||||||
if (Settings.instance.playback.shouldResumePreview(time))
|
if (Settings.instance.playback.shouldResumePreview(time))
|
||||||
fragment.navigate<VideoDetailFragment>(content.withTimestamp(time)).maximizeVideoDetail();
|
fragment.navigate<VideoDetailFragment>(content.withTimestamp(time)).maximizeVideoDetail(false);
|
||||||
else
|
else
|
||||||
fragment.navigate<VideoDetailFragment>(content).maximizeVideoDetail();
|
fragment.navigate<VideoDetailFragment>(content).maximizeVideoDetail(false);
|
||||||
}
|
}
|
||||||
} else if (content is IPlatformPlaylist) {
|
} else if (content is IPlatformPlaylist) {
|
||||||
fragment.navigate<RemotePlaylistFragment>(content);
|
fragment.navigate<RemotePlaylistFragment>(content);
|
||||||
|
@ -202,7 +202,7 @@ abstract class ContentFeedView<TFragment> : FeedView<TFragment, IPlatformContent
|
||||||
when(contentType) {
|
when(contentType) {
|
||||||
ContentType.MEDIA -> {
|
ContentType.MEDIA -> {
|
||||||
StatePlayer.instance.clearQueue();
|
StatePlayer.instance.clearQueue();
|
||||||
fragment.navigate<VideoDetailFragment>(url).maximizeVideoDetail();
|
fragment.navigate<VideoDetailFragment>(url).maximizeVideoDetail(false);
|
||||||
};
|
};
|
||||||
ContentType.PLAYLIST -> fragment.navigate<RemotePlaylistFragment>(url);
|
ContentType.PLAYLIST -> fragment.navigate<RemotePlaylistFragment>(url);
|
||||||
ContentType.URL -> fragment.navigate<BrowserFragment>(url);
|
ContentType.URL -> fragment.navigate<BrowserFragment>(url);
|
||||||
|
|
|
@ -153,7 +153,7 @@ class DownloadsFragment : MainFragment() {
|
||||||
.asAnyWithTop(findViewById(R.id.downloads_top)) {
|
.asAnyWithTop(findViewById(R.id.downloads_top)) {
|
||||||
it.onClick.subscribe {
|
it.onClick.subscribe {
|
||||||
StatePlayer.instance.clearQueue();
|
StatePlayer.instance.clearQueue();
|
||||||
_frag.navigate<VideoDetailFragment>(it).maximizeVideoDetail();
|
_frag.navigate<VideoDetailFragment>(it).maximizeVideoDetail(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -217,7 +217,7 @@ class HistoryFragment : MainFragment() {
|
||||||
val diff = v.video.duration - v.position;
|
val diff = v.video.duration - v.position;
|
||||||
val vid: Any = if (diff > 5) { v.video.withTimestamp(v.position) } else { v.video };
|
val vid: Any = if (diff > 5) { v.video.withTimestamp(v.position) } else { v.video };
|
||||||
StatePlayer.instance.clearQueue();
|
StatePlayer.instance.clearQueue();
|
||||||
_fragment.navigate<VideoDetailFragment>(vid).maximizeVideoDetail();
|
_fragment.navigate<VideoDetailFragment>(vid).maximizeVideoDetail(false);
|
||||||
_editSearch.clearFocus();
|
_editSearch.clearFocus();
|
||||||
inputMethodManager.hideSoftInputFromWindow(_editSearch.windowToken, 0);
|
inputMethodManager.hideSoftInputFromWindow(_editSearch.windowToken, 0);
|
||||||
|
|
||||||
|
|
|
@ -34,13 +34,12 @@ import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.models.PlatformVideoWithTime
|
import com.futo.platformplayer.models.PlatformVideoWithTime
|
||||||
import com.futo.platformplayer.models.UrlVideoWithTime
|
import com.futo.platformplayer.models.UrlVideoWithTime
|
||||||
import com.futo.platformplayer.states.StatePlayer
|
import com.futo.platformplayer.states.StatePlayer
|
||||||
import com.futo.platformplayer.views.containers.SingleViewTouchableMotionLayout
|
import com.futo.platformplayer.views.containers.CustomMotionLayout
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
|
||||||
//region Fragment
|
//region Fragment
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
class VideoDetailFragment() : MainFragment() {
|
class VideoDetailFragment() : MainFragment() {
|
||||||
|
@ -51,8 +50,8 @@ class VideoDetailFragment() : MainFragment() {
|
||||||
|
|
||||||
private var _isActive: Boolean = false;
|
private var _isActive: Boolean = false;
|
||||||
|
|
||||||
private var _viewDetail : VideoDetailView? = null;
|
private var _viewDetail : VideoDetailView? = null
|
||||||
private var _view : SingleViewTouchableMotionLayout? = null;
|
private var _motionLayout: CustomMotionLayout? = null
|
||||||
|
|
||||||
var isFullscreen : Boolean = false;
|
var isFullscreen : Boolean = false;
|
||||||
/**
|
/**
|
||||||
|
@ -61,8 +60,6 @@ class VideoDetailFragment() : MainFragment() {
|
||||||
*/
|
*/
|
||||||
var isMinimizingFromFullScreen : Boolean = false;
|
var isMinimizingFromFullScreen : Boolean = false;
|
||||||
val onFullscreenChanged = Event1<Boolean>();
|
val onFullscreenChanged = Event1<Boolean>();
|
||||||
var isTransitioning : Boolean = false
|
|
||||||
private set;
|
|
||||||
var isInPictureInPicture : Boolean = false
|
var isInPictureInPicture : Boolean = false
|
||||||
private set;
|
private set;
|
||||||
|
|
||||||
|
@ -78,13 +75,8 @@ class VideoDetailFragment() : MainFragment() {
|
||||||
val currentUrl get() = _viewDetail?.currentUrl;
|
val currentUrl get() = _viewDetail?.currentUrl;
|
||||||
|
|
||||||
val onMinimize = Event0();
|
val onMinimize = Event0();
|
||||||
val onTransitioning = Event1<Boolean>();
|
|
||||||
val onMaximized = Event0();
|
val onMaximized = Event0();
|
||||||
|
|
||||||
private var _isInitialMaximize = true;
|
|
||||||
|
|
||||||
private val _maximizeProgress get() = _view?.progress ?: 0.0f;
|
|
||||||
|
|
||||||
private var _loadUrlOnCreate: UrlVideoWithTime? = null;
|
private var _loadUrlOnCreate: UrlVideoWithTime? = null;
|
||||||
private var _leavingPiP = false;
|
private var _leavingPiP = false;
|
||||||
|
|
||||||
|
@ -295,25 +287,21 @@ class VideoDetailFragment() : MainFragment() {
|
||||||
|
|
||||||
fun minimizeVideoDetail() {
|
fun minimizeVideoDetail() {
|
||||||
_viewDetail?.setFullscreen(false);
|
_viewDetail?.setFullscreen(false);
|
||||||
if(_view != null)
|
_motionLayout?.transitionToState(R.id.collapsed)
|
||||||
_view!!.transitionToStart();
|
|
||||||
}
|
}
|
||||||
fun maximizeVideoDetail(instant: Boolean = false) {
|
fun maximizeVideoDetail(instant: Boolean) {
|
||||||
if((_maximizeProgress > 0.9f || instant) && state != State.MAXIMIZED) {
|
state = State.MAXIMIZED
|
||||||
state = State.MAXIMIZED;
|
onMaximized.emit()
|
||||||
onMaximized.emit();
|
if(instant) {
|
||||||
|
_motionLayout?.setTransition(R.id.maximize)
|
||||||
|
_motionLayout?.progress = 1f
|
||||||
|
} else {
|
||||||
|
_motionLayout?.transitionToState(R.id.expanded)
|
||||||
}
|
}
|
||||||
_view?.let {
|
|
||||||
if(!instant) {
|
|
||||||
it.transitionToEnd();
|
|
||||||
} else {
|
|
||||||
it.progress = 1f;
|
|
||||||
onTransitioning.emit(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
fun closeVideoDetails() {
|
fun closeVideoDetails() {
|
||||||
Logger.i(TAG, "closeVideoDetails()")
|
Logger.i(TAG, "closeVideoDetails()")
|
||||||
|
_viewDetail?.onPlayChanged?.emit(false)
|
||||||
state = State.CLOSED;
|
state = State.CLOSED;
|
||||||
_viewDetail?.onStop();
|
_viewDetail?.onStop();
|
||||||
close();
|
close();
|
||||||
|
@ -323,90 +311,53 @@ class VideoDetailFragment() : MainFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
_view = inflater.inflate(R.layout.fragment_video_detail, container, false) as SingleViewTouchableMotionLayout;
|
val viewDetail = VideoDetailView(this, inflater);
|
||||||
_viewDetail = _view!!.findViewById<VideoDetailView>(R.id.fragview_videodetail).also {
|
|
||||||
it.applyFragment(this);
|
_motionLayout = viewDetail.findViewById(R.id.videodetail_root)
|
||||||
it.onFullscreenChanged.subscribe(::onFullscreenChanged);
|
viewDetail.applyFragment(this);
|
||||||
it.onVideoChanged.subscribe(::onVideoChanged)
|
viewDetail.onFullscreenChanged.subscribe(::onFullscreenChanged);
|
||||||
it.onMinimize.subscribe {
|
viewDetail.onVideoChanged.subscribe(::onVideoChanged)
|
||||||
isMinimizingFromFullScreen = true
|
viewDetail.onMinimize.subscribe {
|
||||||
_view!!.transitionToStart();
|
isMinimizingFromFullScreen = true
|
||||||
};
|
_motionLayout?.transitionToState(R.id.collapsed)
|
||||||
it.onClose.subscribe {
|
};
|
||||||
Logger.i(TAG, "onClose")
|
viewDetail.onClose.subscribe {
|
||||||
closeVideoDetails();
|
Logger.i(TAG, "onClose")
|
||||||
};
|
closeVideoDetails();
|
||||||
it.onMaximize.subscribe { maximizeVideoDetail(it) };
|
};
|
||||||
it.onPlayChanged.subscribe {
|
viewDetail.onMaximize.subscribe { maximizeVideoDetail(it) };
|
||||||
if(isInPictureInPicture) {
|
viewDetail.onPlayChanged.subscribe {
|
||||||
val params = _viewDetail?.getPictureInPictureParams();
|
val params = _viewDetail?.getPictureInPictureParams(it)
|
||||||
if (params != null)
|
if (params != null)
|
||||||
activity?.setPictureInPictureParams(params);
|
activity?.setPictureInPictureParams(params)
|
||||||
}
|
|
||||||
};
|
|
||||||
it.onEnterPictureInPicture.subscribe {
|
|
||||||
Logger.i(TAG, "onEnterPictureInPicture")
|
|
||||||
isInPictureInPicture = true;
|
|
||||||
_viewDetail?.handleEnterPictureInPicture();
|
|
||||||
_viewDetail?.invalidate();
|
|
||||||
};
|
|
||||||
it.onTouchCancel.subscribe {
|
|
||||||
val v = _view ?: return@subscribe;
|
|
||||||
if (v.progress >= 0.5 && v.progress < 1) {
|
|
||||||
maximizeVideoDetail();
|
|
||||||
}
|
|
||||||
if (v.progress < 0.5 && v.progress > 0) {
|
|
||||||
minimizeVideoDetail();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
_view!!.setTransitionListener(object : MotionLayout.TransitionListener {
|
viewDetail.onEnterPictureInPicture.subscribe {
|
||||||
override fun onTransitionChange(motionLayout: MotionLayout?, startId: Int, endId: Int, progress: Float) {
|
Logger.i(TAG, "onEnterPictureInPicture")
|
||||||
|
isInPictureInPicture = true;
|
||||||
|
_viewDetail?.handleEnterPictureInPicture();
|
||||||
|
_viewDetail?.invalidate();
|
||||||
|
};
|
||||||
|
_motionLayout!!.addTransitionListener(object : MotionLayout.TransitionListener {
|
||||||
|
override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
|
||||||
_viewDetail?.stopAllGestures()
|
_viewDetail?.stopAllGestures()
|
||||||
|
if (state != State.MINIMIZED && currentId == R.id.collapsed) {
|
||||||
if (state != State.MINIMIZED && progress < 0.1) {
|
state = State.MINIMIZED
|
||||||
state = State.MINIMIZED;
|
|
||||||
isMinimizingFromFullScreen = false
|
isMinimizingFromFullScreen = false
|
||||||
onMinimize.emit();
|
onMinimize.emit()
|
||||||
}
|
|
||||||
else if (state != State.MAXIMIZED && progress > 0.9) {
|
|
||||||
if (_isInitialMaximize) {
|
|
||||||
state = State.CLOSED;
|
|
||||||
_isInitialMaximize = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
state = State.MAXIMIZED;
|
|
||||||
onMaximized.emit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTransitioning && (progress > 0.95 || progress < 0.05)) {
|
if (state != State.MAXIMIZED && currentId == R.id.expanded) {
|
||||||
isTransitioning = false;
|
state = State.MAXIMIZED
|
||||||
onTransitioning.emit(isTransitioning);
|
onMaximized.emit()
|
||||||
|
|
||||||
if(isInPictureInPicture) leavePictureInPictureMode(false); //Workaround to prevent getting stuck in p2p
|
|
||||||
}
|
|
||||||
else if (!isTransitioning && (progress < 0.95 && progress > 0.05)) {
|
|
||||||
isTransitioning = true;
|
|
||||||
onTransitioning.emit(isTransitioning);
|
|
||||||
|
|
||||||
if(isInPictureInPicture) leavePictureInPictureMode(false); //Workaround to prevent getting stuck in p2p
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) { }
|
override fun onTransitionStarted(motionLayout: MotionLayout?, startId: Int, endId: Int) {}
|
||||||
override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) { }
|
override fun onTransitionTrigger(motionLayout: MotionLayout?, triggerId: Int, positive: Boolean, progress: Float) {}
|
||||||
override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) { }
|
override fun onTransitionChange(motionLayout: MotionLayout?, startId: Int, endId: Int, progress: Float) {}
|
||||||
});
|
})
|
||||||
|
|
||||||
_view?.let {
|
|
||||||
if (it.progress >= 0.5 && it.progress < 1.0)
|
|
||||||
maximizeVideoDetail();
|
|
||||||
if (it.progress < 0.5 && it.progress > 0.0)
|
|
||||||
minimizeVideoDetail();
|
|
||||||
}
|
|
||||||
|
|
||||||
_loadUrlOnCreate?.let { _viewDetail?.setVideo(it.url, it.timeSeconds, it.playWhenReady) };
|
_loadUrlOnCreate?.let { _viewDetail?.setVideo(it.url, it.timeSeconds, it.playWhenReady) };
|
||||||
maximizeVideoDetail();
|
maximizeVideoDetail(false);
|
||||||
|
|
||||||
SettingsActivity.settingsActivityClosed.subscribe(this) {
|
SettingsActivity.settingsActivityClosed.subscribe(this) {
|
||||||
updateOrientation()
|
updateOrientation()
|
||||||
|
@ -439,17 +390,18 @@ class VideoDetailFragment() : MainFragment() {
|
||||||
}
|
}
|
||||||
_autoRotateObserver?.startObserving()
|
_autoRotateObserver?.startObserving()
|
||||||
|
|
||||||
return _view!!;
|
_viewDetail = viewDetail
|
||||||
|
return viewDetail
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onUserLeaveHint() {
|
fun onUserLeaveHint() {
|
||||||
val viewDetail = _viewDetail;
|
val viewDetail = _viewDetail;
|
||||||
Logger.i(TAG, "onUserLeaveHint preventPictureInPicture=${viewDetail?.preventPictureInPicture} isCasting=${StateCasting.instance.isCasting} isBackgroundPictureInPicture=${Settings.instance.playback.isBackgroundPictureInPicture()} allowBackground=${viewDetail?.allowBackground}");
|
Logger.i(TAG, "onUserLeaveHint preventPictureInPicture=${viewDetail?.preventPictureInPicture} isCasting=${StateCasting.instance.isCasting} isBackgroundPictureInPicture=${Settings.instance.playback.isBackgroundPictureInPicture()} allowBackground=${viewDetail?.allowBackground}");
|
||||||
|
|
||||||
if(viewDetail?.preventPictureInPicture == false && !StateCasting.instance.isCasting && Settings.instance.playback.isBackgroundPictureInPicture() && !viewDetail.allowBackground) {
|
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.S && viewDetail?.preventPictureInPicture == false && !StateCasting.instance.isCasting && Settings.instance.playback.isBackgroundPictureInPicture() && !viewDetail.allowBackground) {
|
||||||
_leavingPiP = false;
|
_leavingPiP = false;
|
||||||
|
|
||||||
val params = _viewDetail?.getPictureInPictureParams();
|
val params = _viewDetail?.getPictureInPictureParams(true);
|
||||||
if(params != null) {
|
if(params != null) {
|
||||||
Logger.i(TAG, "enterPictureInPictureMode")
|
Logger.i(TAG, "enterPictureInPictureMode")
|
||||||
activity?.enterPictureInPictureMode(params);
|
activity?.enterPictureInPictureMode(params);
|
||||||
|
@ -458,7 +410,7 @@ class VideoDetailFragment() : MainFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun forcePictureInPicture() {
|
fun forcePictureInPicture() {
|
||||||
val params = _viewDetail?.getPictureInPictureParams();
|
val params = _viewDetail?.getPictureInPictureParams(true);
|
||||||
if(params != null)
|
if(params != null)
|
||||||
activity?.enterPictureInPictureMode(params);
|
activity?.enterPictureInPictureMode(params);
|
||||||
}
|
}
|
||||||
|
@ -544,21 +496,12 @@ class VideoDetailFragment() : MainFragment() {
|
||||||
_portraitOrientationListener?.disableListener()
|
_portraitOrientationListener?.disableListener()
|
||||||
_autoRotateObserver?.stopObserving()
|
_autoRotateObserver?.stopObserving()
|
||||||
|
|
||||||
_viewDetail?.let {
|
_viewDetail = null;
|
||||||
_viewDetail = null;
|
|
||||||
it.onDestroy();
|
|
||||||
}
|
|
||||||
_view = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
|
|
||||||
_viewDetail?.let {
|
|
||||||
_viewDetail = null;
|
|
||||||
it.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
StateCasting.instance.onActiveDeviceConnectionStateChanged.remove(this);
|
StateCasting.instance.onActiveDeviceConnectionStateChanged.remove(this);
|
||||||
|
|
||||||
Logger.i(TAG, "onDestroy");
|
Logger.i(TAG, "onDestroy");
|
||||||
|
@ -615,13 +558,8 @@ class VideoDetailFragment() : MainFragment() {
|
||||||
showSystemUI()
|
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();
|
updateOrientation();
|
||||||
_view?.allowMotion = !fullscreen;
|
_motionLayout?.isInteractionEnabled = !fullscreen
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -2,7 +2,6 @@ package com.futo.platformplayer.fragment.mainactivity.main
|
||||||
|
|
||||||
import android.app.PictureInPictureParams
|
import android.app.PictureInPictureParams
|
||||||
import android.app.RemoteAction
|
import android.app.RemoteAction
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
|
@ -13,13 +12,13 @@ import android.graphics.drawable.BitmapDrawable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.graphics.drawable.Icon
|
import android.graphics.drawable.Icon
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.support.v4.media.session.PlaybackStateCompat
|
import android.support.v4.media.session.PlaybackStateCompat
|
||||||
import android.text.Spanned
|
import android.text.Spanned
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.Rational
|
import android.util.Rational
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.MotionEvent
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
@ -30,6 +29,7 @@ import android.widget.ImageButton
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.media3.common.C
|
import androidx.media3.common.C
|
||||||
|
@ -130,9 +130,9 @@ import com.futo.platformplayer.views.FeedStyle
|
||||||
import com.futo.platformplayer.views.LoaderView
|
import com.futo.platformplayer.views.LoaderView
|
||||||
import com.futo.platformplayer.views.MonetizationView
|
import com.futo.platformplayer.views.MonetizationView
|
||||||
import com.futo.platformplayer.views.adapters.feedtypes.PreviewVideoView
|
import com.futo.platformplayer.views.adapters.feedtypes.PreviewVideoView
|
||||||
import com.futo.platformplayer.views.behavior.TouchInterceptFrameLayout
|
|
||||||
import com.futo.platformplayer.views.casting.CastView
|
import com.futo.platformplayer.views.casting.CastView
|
||||||
import com.futo.platformplayer.views.comments.AddCommentView
|
import com.futo.platformplayer.views.comments.AddCommentView
|
||||||
|
import com.futo.platformplayer.views.containers.CustomMotionLayout
|
||||||
import com.futo.platformplayer.views.others.CreatorThumbnail
|
import com.futo.platformplayer.views.others.CreatorThumbnail
|
||||||
import com.futo.platformplayer.views.overlays.DescriptionOverlay
|
import com.futo.platformplayer.views.overlays.DescriptionOverlay
|
||||||
import com.futo.platformplayer.views.overlays.LiveChatOverlay
|
import com.futo.platformplayer.views.overlays.LiveChatOverlay
|
||||||
|
@ -160,6 +160,7 @@ import com.futo.polycentric.core.Models
|
||||||
import com.futo.polycentric.core.Opinion
|
import com.futo.polycentric.core.Opinion
|
||||||
import com.futo.polycentric.core.toURLInfoSystemLinkUrl
|
import com.futo.polycentric.core.toURLInfoSystemLinkUrl
|
||||||
import com.google.protobuf.ByteString
|
import com.google.protobuf.ByteString
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
@ -174,7 +175,7 @@ import kotlin.math.abs
|
||||||
import kotlin.math.roundToLong
|
import kotlin.math.roundToLong
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
class VideoDetailView : ConstraintLayout {
|
class VideoDetailView(fragment: VideoDetailFragment, inflater: LayoutInflater) : FrameLayout(inflater.context) {
|
||||||
private val TAG = "VideoDetailView"
|
private val TAG = "VideoDetailView"
|
||||||
|
|
||||||
lateinit var fragment: VideoDetailFragment;
|
lateinit var fragment: VideoDetailFragment;
|
||||||
|
@ -201,7 +202,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
private val _timeBar: TimeBar;
|
private val _timeBar: TimeBar;
|
||||||
private var _upNext: UpNextView;
|
private var _upNext: UpNextView;
|
||||||
|
|
||||||
private val rootView: ConstraintLayout;
|
private val rootView: CustomMotionLayout;
|
||||||
|
|
||||||
private val _title: TextView;
|
private val _title: TextView;
|
||||||
private val _subTitle: TextView;
|
private val _subTitle: TextView;
|
||||||
|
@ -230,7 +231,6 @@ class VideoDetailView : ConstraintLayout {
|
||||||
|
|
||||||
private val _commentsList: CommentsList;
|
private val _commentsList: CommentsList;
|
||||||
|
|
||||||
private var _minimizeProgress: Float = 0f;
|
|
||||||
private val _buttonSubscribe: SubscribeButton;
|
private val _buttonSubscribe: SubscribeButton;
|
||||||
|
|
||||||
private val _buttonPins: RoundButtonGroup;
|
private val _buttonPins: RoundButtonGroup;
|
||||||
|
@ -246,7 +246,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
private val _textResume: TextView;
|
private val _textResume: TextView;
|
||||||
private val _layoutResume: LinearLayout;
|
private val _layoutResume: LinearLayout;
|
||||||
private var _jobHideResume: Job? = null;
|
private var _jobHideResume: Job? = null;
|
||||||
private val _layoutPlayerContainer: TouchInterceptFrameLayout;
|
private val _layoutPlayerContainer: FrameLayout;
|
||||||
private val _layoutChangeBottomSection: LinearLayout;
|
private val _layoutChangeBottomSection: LinearLayout;
|
||||||
|
|
||||||
//Overlays
|
//Overlays
|
||||||
|
@ -310,7 +310,6 @@ class VideoDetailView : ConstraintLayout {
|
||||||
var allowBackground : Boolean = false
|
var allowBackground : Boolean = false
|
||||||
private set;
|
private set;
|
||||||
|
|
||||||
val onTouchCancel = Event0();
|
|
||||||
private var _lastPositionSaveTime: Long = -1;
|
private var _lastPositionSaveTime: Long = -1;
|
||||||
|
|
||||||
private val DP_5 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics);
|
private val DP_5 = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics);
|
||||||
|
@ -328,9 +327,8 @@ class VideoDetailView : ConstraintLayout {
|
||||||
Pair(0, 10) //around live, try every 10 seconds
|
Pair(0, 10) //around live, try every 10 seconds
|
||||||
);
|
);
|
||||||
|
|
||||||
@androidx.annotation.OptIn(UnstableApi::class)
|
init {
|
||||||
constructor(context: Context, attrs : AttributeSet? = null) : super(context, attrs) {
|
inflater.inflate(R.layout.fragview_video_detail, this)
|
||||||
inflate(context, R.layout.fragview_video_detail, this);
|
|
||||||
|
|
||||||
//Declare Views
|
//Declare Views
|
||||||
rootView = findViewById(R.id.videodetail_root);
|
rootView = findViewById(R.id.videodetail_root);
|
||||||
|
@ -382,8 +380,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
_textSkip = findViewById(R.id.text_skip);
|
_textSkip = findViewById(R.id.text_skip);
|
||||||
_layoutResume = findViewById(R.id.layout_resume);
|
_layoutResume = findViewById(R.id.layout_resume);
|
||||||
_textResume = findViewById(R.id.text_resume);
|
_textResume = findViewById(R.id.text_resume);
|
||||||
_layoutPlayerContainer = findViewById(R.id.layout_player_container);
|
_layoutPlayerContainer = findViewById(R.id.layout_player_container)
|
||||||
_layoutPlayerContainer.onClick.subscribe { onMaximize.emit(false); };
|
|
||||||
|
|
||||||
_layoutRating = findViewById(R.id.layout_rating);
|
_layoutRating = findViewById(R.id.layout_rating);
|
||||||
_textDislikes = findViewById(R.id.text_dislikes);
|
_textDislikes = findViewById(R.id.text_dislikes);
|
||||||
|
@ -548,10 +545,6 @@ class VideoDetailView : ConstraintLayout {
|
||||||
updatePlaybackTracking(position);
|
updatePlaybackTracking(position);
|
||||||
};
|
};
|
||||||
|
|
||||||
_player.onVideoClicked.subscribe {
|
|
||||||
if(_minimizeProgress < 0.5)
|
|
||||||
onMaximize.emit(false);
|
|
||||||
}
|
|
||||||
_player.onSourceChanged.subscribe(::onSourceChanged);
|
_player.onSourceChanged.subscribe(::onSourceChanged);
|
||||||
_player.onSourceEnded.subscribe {
|
_player.onSourceEnded.subscribe {
|
||||||
if (!fragment.isInPictureInPicture) {
|
if (!fragment.isInPictureInPicture) {
|
||||||
|
@ -790,6 +783,48 @@ class VideoDetailView : ConstraintLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var currentState = R.id.expanded
|
||||||
|
|
||||||
|
rootView.addTransitionListener(object : MotionLayout.TransitionListener {
|
||||||
|
override fun onTransitionCompleted(motionLayout: MotionLayout?, currentId: Int) {
|
||||||
|
when (currentId) {
|
||||||
|
R.id.collapsed -> {
|
||||||
|
_player.gestureControl.setOnClickListener {
|
||||||
|
fragment.maximizeVideoDetail(false)
|
||||||
|
}
|
||||||
|
_player.gestureControl.controlsEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
R.id.expanded -> {
|
||||||
|
_layoutResume.alpha = 1f
|
||||||
|
_cast.setButtonAlpha(1f)
|
||||||
|
_player.lockControlsAlpha(false)
|
||||||
|
_player.hideControls(false)
|
||||||
|
|
||||||
|
_player.gestureControl.controlsEnabled = true
|
||||||
|
_player.gestureControl.setOnClickListener(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentState = currentId
|
||||||
|
if(currentId == R.id.full_screen_gesture) {
|
||||||
|
setFullscreen(true)
|
||||||
|
motionLayout?.transitionToState(R.id.expanded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTransitionStarted(motionLayout: MotionLayout?, startId: Int, endId: Int) {
|
||||||
|
if (currentState == R.id.expanded) {
|
||||||
|
_layoutResume.alpha = 0f
|
||||||
|
_cast.setButtonAlpha(0f)
|
||||||
|
_player.lockControlsAlpha(true)
|
||||||
|
_player.hideControls(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTransitionTrigger(motionLayout: MotionLayout?, triggerId: Int, positive: Boolean, progress: Float) { }
|
||||||
|
override fun onTransitionChange(motionLayout: MotionLayout?, startId: Int, endId: Int, progress: Float) {}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
val _trackingUpdateTimeLock = Object();
|
val _trackingUpdateTimeLock = Object();
|
||||||
|
@ -1920,16 +1955,6 @@ class VideoDetailView : ConstraintLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
|
|
||||||
if (ev?.actionMasked == MotionEvent.ACTION_CANCEL ||
|
|
||||||
ev?.actionMasked == MotionEvent.ACTION_POINTER_DOWN ||
|
|
||||||
ev?.actionMasked == MotionEvent.ACTION_POINTER_UP) {
|
|
||||||
onTouchCancel.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.onInterceptTouchEvent(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Actions
|
//Actions
|
||||||
private fun showVideoSettings() {
|
private fun showVideoSettings() {
|
||||||
Logger.i(TAG, "showVideoSettings")
|
Logger.i(TAG, "showVideoSettings")
|
||||||
|
@ -2387,9 +2412,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
Logger.i(TAG, "handleFullScreen(fullscreen=$fullscreen)")
|
Logger.i(TAG, "handleFullScreen(fullscreen=$fullscreen)")
|
||||||
|
|
||||||
if(fullscreen) {
|
if(fullscreen) {
|
||||||
_layoutPlayerContainer.setPadding(0, 0, 0, 0);
|
val lp = _container_content.layoutParams as ConstraintLayout.LayoutParams
|
||||||
|
|
||||||
val lp = _container_content.layoutParams as LayoutParams;
|
|
||||||
lp.topMargin = 0;
|
lp.topMargin = 0;
|
||||||
_container_content.layoutParams = lp;
|
_container_content.layoutParams = lp;
|
||||||
|
|
||||||
|
@ -2400,9 +2423,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
setProgressBarOverlayed(null);
|
setProgressBarOverlayed(null);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
_layoutPlayerContainer.setPadding(0, 0, 0, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6.0f, Resources.getSystem().displayMetrics).toInt());
|
val lp = _container_content.layoutParams as ConstraintLayout.LayoutParams;
|
||||||
|
|
||||||
val lp = _container_content.layoutParams as LayoutParams;
|
|
||||||
lp.topMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, -18.0f, Resources.getSystem().displayMetrics).toInt();
|
lp.topMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, -18.0f, Resources.getSystem().displayMetrics).toInt();
|
||||||
_container_content.layoutParams = lp;
|
_container_content.layoutParams = lp;
|
||||||
|
|
||||||
|
@ -2618,7 +2639,7 @@ class VideoDetailView : ConstraintLayout {
|
||||||
hideAddTo()
|
hideAddTo()
|
||||||
|
|
||||||
onVideoClicked.subscribe { video, _ ->
|
onVideoClicked.subscribe { video, _ ->
|
||||||
fragment.navigate<VideoDetailFragment>(video).maximizeVideoDetail()
|
fragment.navigate<VideoDetailFragment>(video).maximizeVideoDetail(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
onChannelClicked.subscribe {
|
onChannelClicked.subscribe {
|
||||||
|
@ -2658,19 +2679,11 @@ class VideoDetailView : ConstraintLayout {
|
||||||
_overlay_quality_selector?.hide();
|
_overlay_quality_selector?.hide();
|
||||||
|
|
||||||
_player.fillHeight(false)
|
_player.fillHeight(false)
|
||||||
_layoutPlayerContainer.setPadding(0, 0, 0, 0);
|
|
||||||
}
|
}
|
||||||
fun handleLeavePictureInPicture() {
|
fun handleLeavePictureInPicture() {
|
||||||
Logger.i(TAG, "handleLeavePictureInPicture")
|
Logger.i(TAG, "handleLeavePictureInPicture")
|
||||||
|
|
||||||
if(!_player.isFullScreen) {
|
|
||||||
_player.fitHeight();
|
|
||||||
_layoutPlayerContainer.setPadding(0, 0, 0, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6.0f, Resources.getSystem().displayMetrics).toInt());
|
|
||||||
} else {
|
|
||||||
_layoutPlayerContainer.setPadding(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fun getPictureInPictureParams() : PictureInPictureParams {
|
fun getPictureInPictureParams(isPlaying: Boolean) : PictureInPictureParams {
|
||||||
var videoSourceWidth = _player.exoPlayer?.player?.videoSize?.width ?: 0;
|
var videoSourceWidth = _player.exoPlayer?.player?.videoSize?.width ?: 0;
|
||||||
var videoSourceHeight = _player.exoPlayer?.player?.videoSize?.height ?: 0;
|
var videoSourceHeight = _player.exoPlayer?.player?.videoSize?.height ?: 0;
|
||||||
|
|
||||||
|
@ -2696,11 +2709,16 @@ class VideoDetailView : ConstraintLayout {
|
||||||
else
|
else
|
||||||
RemoteAction(Icon.createWithResource(context, R.drawable.ic_play_notif), context.getString(R.string.play), context.getString(R.string.resumes_the_video), MediaControlReceiver.getPlayIntent(context, 6));
|
RemoteAction(Icon.createWithResource(context, R.drawable.ic_play_notif), context.getString(R.string.play), context.getString(R.string.resumes_the_video), MediaControlReceiver.getPlayIntent(context, 6));
|
||||||
|
|
||||||
return PictureInPictureParams.Builder()
|
val params = PictureInPictureParams.Builder()
|
||||||
.setAspectRatio(Rational(videoSourceWidth, videoSourceHeight))
|
.setAspectRatio(Rational(videoSourceWidth, videoSourceHeight))
|
||||||
.setSourceRectHint(r)
|
.setSourceRectHint(r)
|
||||||
.setActions(listOf(playpauseAction))
|
.setActions(listOf(playpauseAction))
|
||||||
.build();
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
params.setAutoEnterEnabled(isPlaying)
|
||||||
|
}
|
||||||
|
|
||||||
|
return params.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
//Other
|
//Other
|
||||||
|
@ -2758,53 +2776,6 @@ class VideoDetailView : ConstraintLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Animation related setters
|
|
||||||
fun setMinimizeProgress(progress : Float) {
|
|
||||||
_minimizeProgress = progress;
|
|
||||||
_player.lockControlsAlpha(progress < 0.9);
|
|
||||||
_layoutPlayerContainer.shouldInterceptTouches = progress < 0.95;
|
|
||||||
|
|
||||||
if(progress > 0.9) {
|
|
||||||
if(_minimize_controls.visibility != View.GONE)
|
|
||||||
_minimize_controls.visibility = View.GONE;
|
|
||||||
}
|
|
||||||
else if(_minimize_controls.visibility != View.VISIBLE) {
|
|
||||||
_minimize_controls.visibility = View.VISIBLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Switching video to fill
|
|
||||||
if(progress > 0.25) {
|
|
||||||
if(!_player.isFullScreen && _player.layoutParams.height != WRAP_CONTENT) {
|
|
||||||
_player.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
|
|
||||||
if(!fragment.isInPictureInPicture) {
|
|
||||||
_player.fitHeight();
|
|
||||||
_layoutPlayerContainer.setPadding(0, 0, 0, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6.0f, Resources.getSystem().displayMetrics).toInt());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_layoutPlayerContainer.setPadding(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
_cast.layoutParams = _cast.layoutParams.apply {
|
|
||||||
(this as MarginLayoutParams).bottomMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6.0f, resources.displayMetrics).toInt();
|
|
||||||
};
|
|
||||||
setProgressBarOverlayed(false);
|
|
||||||
_player.hideControls(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(_player.layoutParams.height == WRAP_CONTENT) {
|
|
||||||
_player.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT);
|
|
||||||
_player.fillHeight(true)
|
|
||||||
_cast.layoutParams = _cast.layoutParams.apply {
|
|
||||||
(this as MarginLayoutParams).bottomMargin = 0;
|
|
||||||
};
|
|
||||||
setProgressBarOverlayed(true);
|
|
||||||
_player.hideControls(false);
|
|
||||||
|
|
||||||
_layoutPlayerContainer.setPadding(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setPolycentricProfile(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
|
private fun setPolycentricProfile(cachedPolycentricProfile: PolycentricCache.CachedPolycentricProfile?, animate: Boolean) {
|
||||||
_polycentricProfile = cachedPolycentricProfile;
|
_polycentricProfile = cachedPolycentricProfile;
|
||||||
|
|
||||||
|
@ -2829,42 +2800,8 @@ class VideoDetailView : ConstraintLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setProgressBarOverlayed(isOverlayed: Boolean?) {
|
fun setProgressBarOverlayed(isOverlayed: Boolean?) {
|
||||||
Logger.v(TAG, "setProgressBarOverlayed(isOverlayed: ${isOverlayed ?: "null"})");
|
Logger.v(TAG, "setProgressBarOverlayed(isOverlayed: ${isOverlayed ?: "null"})")
|
||||||
isOverlayed?.let{ _cast.setProgressBarOverlayed(it) };
|
isOverlayed?.let { _cast.setProgressBarOverlayed(it) }
|
||||||
|
|
||||||
if(isOverlayed == null) {
|
|
||||||
//For now this seems to be the best way to keep it updated?
|
|
||||||
_playerProgress.layoutParams = _playerProgress.layoutParams.apply {
|
|
||||||
(this as MarginLayoutParams).bottomMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, -12f, resources.displayMetrics).toInt();
|
|
||||||
};
|
|
||||||
_playerProgress.elevation = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2f, resources.displayMetrics);
|
|
||||||
}
|
|
||||||
else if(isOverlayed) {
|
|
||||||
_playerProgress.layoutParams = _playerProgress.layoutParams.apply {
|
|
||||||
(this as MarginLayoutParams).bottomMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, -2f, resources.displayMetrics).toInt();
|
|
||||||
};
|
|
||||||
_playerProgress.elevation = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_playerProgress.layoutParams = _playerProgress.layoutParams.apply {
|
|
||||||
(this as MarginLayoutParams).bottomMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6f, resources.displayMetrics).toInt();
|
|
||||||
};
|
|
||||||
_playerProgress.elevation = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2f, resources.displayMetrics);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fun setContentAlpha(alpha: Float) {
|
|
||||||
_container_content.alpha = alpha;
|
|
||||||
}
|
|
||||||
fun setControllerAlpha(alpha: Float) {
|
|
||||||
_layoutResume.alpha = alpha;
|
|
||||||
_player.videoControls.alpha = alpha;
|
|
||||||
_cast.setButtonAlpha(alpha);
|
|
||||||
}
|
|
||||||
fun setMinimizeControlsAlpha(alpha : Float) {
|
|
||||||
_minimize_controls.alpha = alpha;
|
|
||||||
val clickable = alpha > 0.9;
|
|
||||||
if(_minimize_controls.isClickable != clickable)
|
|
||||||
_minimize_controls.isClickable = clickable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onConfigurationChanged(newConfig: Configuration?) {
|
override fun onConfigurationChanged(newConfig: Configuration?) {
|
||||||
|
@ -2876,16 +2813,6 @@ class VideoDetailView : ConstraintLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setVideoMinimize(value : Float) {
|
|
||||||
val padRight = (resources.displayMetrics.widthPixels * 0.70 * value).toInt()
|
|
||||||
_player.setPadding(0, _player.paddingTop, padRight, 0)
|
|
||||||
_cast.setPadding(0, _cast.paddingTop, padRight, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setTopPadding(value: Float) {
|
|
||||||
_player.setPadding(_player.paddingLeft, value.toInt(), _player.paddingRight, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Tasks
|
//Tasks
|
||||||
private val _taskLoadVideo = if(!isInEditMode) TaskHandler<String, IPlatformVideoDetails>(
|
private val _taskLoadVideo = if(!isInEditMode) TaskHandler<String, IPlatformVideoDetails>(
|
||||||
StateApp.instance.scopeGetter,
|
StateApp.instance.scopeGetter,
|
||||||
|
|
|
@ -106,6 +106,8 @@ class GestureControlView : LinearLayout {
|
||||||
|
|
||||||
var fullScreenGestureEnabled = true
|
var fullScreenGestureEnabled = true
|
||||||
|
|
||||||
|
var controlsEnabled = true
|
||||||
|
|
||||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
|
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
|
||||||
LayoutInflater.from(context).inflate(R.layout.view_gesture_controls, this, true);
|
LayoutInflater.from(context).inflate(R.layout.view_gesture_controls, this, true);
|
||||||
|
|
||||||
|
@ -309,6 +311,10 @@ class GestureControlView : LinearLayout {
|
||||||
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
||||||
val ev = event ?: return super.onTouchEvent(event);
|
val ev = event ?: return super.onTouchEvent(event);
|
||||||
|
|
||||||
|
if(!controlsEnabled) {
|
||||||
|
return super.onTouchEvent(ev)
|
||||||
|
}
|
||||||
|
|
||||||
cancelHideJob();
|
cancelHideJob();
|
||||||
|
|
||||||
if (_skipping) {
|
if (_skipping) {
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
package com.futo.platformplayer.views.containers
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewConfiguration
|
||||||
|
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||||
|
import com.futo.platformplayer.R
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
class CustomMotionLayout(context: Context, attributeSet: AttributeSet? = null) :
|
||||||
|
MotionLayout(context, attributeSet) {
|
||||||
|
|
||||||
|
private val viewToDetectTouch by lazy {
|
||||||
|
findViewById<View>(R.id.layout_player_container) //TODO move to Attributes
|
||||||
|
}
|
||||||
|
private val viewToDetectTouch2 by lazy {
|
||||||
|
findViewById<View>(R.id.minimize_controls) //TODO move to Attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
private var savedActionDown: MotionEvent? = null
|
||||||
|
private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop
|
||||||
|
|
||||||
|
// intercepting touch events is necessary because something to do with PlayerControlView makes things not work
|
||||||
|
override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
|
||||||
|
val ev = event ?: return super.onInterceptTouchEvent(null)
|
||||||
|
|
||||||
|
// special touch interception logic is unnecessary if interaction is disabled
|
||||||
|
if (!isInteractionEnabled) {
|
||||||
|
return super.onInterceptTouchEvent(ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (ev.actionMasked) {
|
||||||
|
MotionEvent.ACTION_DOWN -> {
|
||||||
|
val viewRect = Rect()
|
||||||
|
viewToDetectTouch.getHitRect(viewRect)
|
||||||
|
val isInView = viewRect.contains(ev.x.toInt(), ev.y.toInt())
|
||||||
|
viewToDetectTouch2.getHitRect(viewRect)
|
||||||
|
val isInView2 = viewRect.contains(ev.x.toInt(), ev.y.toInt())
|
||||||
|
|
||||||
|
// Don't intercept touches if they are outside of the player or the mini player controls
|
||||||
|
if (!isInView && !isInView2) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
val thing = super.onInterceptTouchEvent(ev)
|
||||||
|
// If the MotionLayout is already intercepting this touch then don't track it
|
||||||
|
if (thing) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MotionLayout didn't intercept the touch but the touch is over the player/mini controls views
|
||||||
|
// in the future the class will
|
||||||
|
// save the touch event for later
|
||||||
|
// need to replay this initial touch to the MotionLayout if it ends up turning into a drag
|
||||||
|
// return false because that matches the return from the super call above
|
||||||
|
savedActionDown?.recycle() // Recycle the old event to prevent memory leaks (if for some reason it wasn't cleaned up in the other code paths)
|
||||||
|
savedActionDown = MotionEvent.obtain(ev)
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionEvent.ACTION_MOVE -> {
|
||||||
|
val localSavedActionDown = savedActionDown
|
||||||
|
|
||||||
|
// only handle the move event if there is a saved action stored
|
||||||
|
// then check to see if it has turned into a drag
|
||||||
|
if (localSavedActionDown != null) {
|
||||||
|
val dy = abs(ev.y - localSavedActionDown.y)
|
||||||
|
if (dy > touchSlop) {
|
||||||
|
// if it has turned into a drag then
|
||||||
|
// replay the down action saved earlier
|
||||||
|
// clean up our data
|
||||||
|
// return true so that the MotionLayout's onTouchEvent will receive future events for this gesture
|
||||||
|
//
|
||||||
|
// it is necessary to replay the down action because otherwise MotionLayout will not always initialize the drag correctly
|
||||||
|
super.onTouchEvent(localSavedActionDown)
|
||||||
|
localSavedActionDown.recycle() // Clean up the saved event after replaying
|
||||||
|
savedActionDown = null
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it's an up or cancel action clean up our tracking
|
||||||
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||||
|
savedActionDown?.recycle()
|
||||||
|
savedActionDown = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// since the function hasn't handled the even this far send it to the parent class
|
||||||
|
return super.onInterceptTouchEvent(ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
// onTouchEvent is necessary to make sure that only touch and drag on the video triggers the animation (instead of everywhere on the screen)
|
||||||
|
@SuppressLint("ClickableViewAccessibility") // pretty sure this issue doesn't apply
|
||||||
|
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
||||||
|
val ev = event ?: return super.onTouchEvent(null)
|
||||||
|
|
||||||
|
// special touch event handling logic is unnecessary if interaction is disabled
|
||||||
|
if (!isInteractionEnabled) {
|
||||||
|
return super.onTouchEvent(ev)
|
||||||
|
}
|
||||||
|
|
||||||
|
val viewRect = Rect()
|
||||||
|
viewToDetectTouch.getHitRect(viewRect)
|
||||||
|
val isInView = viewRect.contains(ev.x.toInt(), ev.y.toInt())
|
||||||
|
viewToDetectTouch2.getHitRect(viewRect)
|
||||||
|
val isInView2 = viewRect.contains(ev.x.toInt(), ev.y.toInt())
|
||||||
|
|
||||||
|
// don't want to handle touches outside of the player/mini controls views
|
||||||
|
if ((!isInView && !isInView2) && event.actionMasked == MotionEvent.ACTION_DOWN) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return super.onTouchEvent(event)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,94 +0,0 @@
|
||||||
package com.futo.platformplayer.views.containers
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Rect
|
|
||||||
import android.util.AttributeSet
|
|
||||||
import android.view.GestureDetector
|
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
|
||||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
|
||||||
import com.futo.platformplayer.R
|
|
||||||
|
|
||||||
class SingleViewTouchableMotionLayout(context: Context, attributeSet: AttributeSet? = null) : MotionLayout(context, attributeSet) {
|
|
||||||
|
|
||||||
private val viewToDetectTouch by lazy {
|
|
||||||
findViewById<View>(R.id.touchContainer) //TODO move to Attributes
|
|
||||||
}
|
|
||||||
private val viewRect = Rect()
|
|
||||||
private var touchStarted = false
|
|
||||||
private val transitionListenerList = mutableListOf<TransitionListener?>()
|
|
||||||
|
|
||||||
var allowMotion : Boolean = true;
|
|
||||||
|
|
||||||
init {
|
|
||||||
addTransitionListener(object : TransitionListener {
|
|
||||||
override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) {
|
|
||||||
touchStarted = false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) {
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) {
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
super.setTransitionListener(object : TransitionListener {
|
|
||||||
override fun onTransitionChange(p0: MotionLayout?, p1: Int, p2: Int, p3: Float) {
|
|
||||||
transitionListenerList.filterNotNull()
|
|
||||||
.forEach { it.onTransitionChange(p0, p1, p2, p3) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTransitionCompleted(p0: MotionLayout?, p1: Int) {
|
|
||||||
transitionListenerList.filterNotNull()
|
|
||||||
.forEach { it.onTransitionCompleted(p0, p1) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTransitionStarted(p0: MotionLayout?, p1: Int, p2: Int) {
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTransitionTrigger(p0: MotionLayout?, p1: Int, p2: Boolean, p3: Float) {
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
//isInteractionEnabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setTransitionListener(listener: TransitionListener?) {
|
|
||||||
addTransitionListener(listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun addTransitionListener(listener: TransitionListener?) {
|
|
||||||
transitionListenerList += listener
|
|
||||||
}
|
|
||||||
|
|
||||||
//This always triggers, workaround calling super.onTouchEvent
|
|
||||||
//Blocks click events underneath
|
|
||||||
override fun onInterceptTouchEvent(event: MotionEvent?): Boolean {
|
|
||||||
if(!allowMotion)
|
|
||||||
return false;
|
|
||||||
if(event != null) {
|
|
||||||
when (event.actionMasked) {
|
|
||||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
|
||||||
touchStarted = false
|
|
||||||
return super.onTouchEvent(event) && false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!touchStarted) {
|
|
||||||
viewToDetectTouch.getHitRect(viewRect);
|
|
||||||
val isInView = viewRect.contains(event.x.toInt(), event.y.toInt());
|
|
||||||
touchStarted = isInView
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return touchStarted && super.onTouchEvent(event) && false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//Not triggered on its own due to child views, intercept is used instead.
|
|
||||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -113,7 +113,6 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||||
private var _lastSourceFit: Float? = null;
|
private var _lastSourceFit: Float? = null;
|
||||||
private var _lastWindowWidth: Int = resources.configuration.screenWidthDp
|
private var _lastWindowWidth: Int = resources.configuration.screenWidthDp
|
||||||
private var _lastWindowHeight: Int = resources.configuration.screenHeightDp
|
private var _lastWindowHeight: Int = resources.configuration.screenHeightDp
|
||||||
private var _originalBottomMargin: Int = 0;
|
|
||||||
|
|
||||||
private var _isControlsLocked: Boolean = false;
|
private var _isControlsLocked: Boolean = false;
|
||||||
|
|
||||||
|
@ -139,10 +138,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||||
val onSourceEnded = Event0();
|
val onSourceEnded = Event0();
|
||||||
val onPrevious = Event0();
|
val onPrevious = Event0();
|
||||||
val onNext = Event0();
|
val onNext = Event0();
|
||||||
|
|
||||||
val onChapterChanged = Event2<IChapter?, Boolean>();
|
val onChapterChanged = Event2<IChapter?, Boolean>();
|
||||||
|
|
||||||
val onVideoClicked = Event0();
|
|
||||||
val onTimeBarChanged = Event2<Long, Long>();
|
val onTimeBarChanged = Event2<Long, Long>();
|
||||||
|
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
|
@ -599,13 +595,10 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fullScreen) {
|
if (fullScreen) {
|
||||||
val lp = background.layoutParams as ConstraintLayout.LayoutParams;
|
_videoView.setPadding(_videoView.paddingLeft, _videoView.paddingTop, _videoView.paddingRight, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.0f, Resources.getSystem().displayMetrics).toInt())
|
||||||
lp.bottomMargin = 0;
|
|
||||||
background.layoutParams = lp;
|
|
||||||
_videoView.setBackgroundColor(Color.parseColor("#FF000000"))
|
_videoView.setBackgroundColor(Color.parseColor("#FF000000"))
|
||||||
|
|
||||||
gestureControl.hideControls();
|
gestureControl.hideControls();
|
||||||
//videoControlsBar.visibility = View.GONE;
|
|
||||||
_videoView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT;
|
_videoView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT;
|
||||||
|
|
||||||
_videoControls_fullscreen.show();
|
_videoControls_fullscreen.show();
|
||||||
|
@ -613,13 +606,10 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||||
videoControls.visibility = View.GONE;
|
videoControls.visibility = View.GONE;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val lp = background.layoutParams as ConstraintLayout.LayoutParams;
|
_videoView.setPadding(_videoView.paddingLeft, _videoView.paddingTop, _videoView.paddingRight, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 7.0f, Resources.getSystem().displayMetrics).toInt())
|
||||||
lp.bottomMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 6.0f, Resources.getSystem().displayMetrics).toInt();
|
|
||||||
background.layoutParams = lp;
|
|
||||||
_videoView.setBackgroundColor(Color.parseColor("#00000000"))
|
_videoView.setBackgroundColor(Color.parseColor("#00000000"))
|
||||||
|
|
||||||
gestureControl.hideControls();
|
gestureControl.hideControls();
|
||||||
//videoControlsBar.visibility = View.VISIBLE;
|
|
||||||
_videoView.resizeMode = _desiredResizeModePortrait;
|
_videoView.resizeMode = _desiredResizeModePortrait;
|
||||||
|
|
||||||
videoControls.show();
|
videoControls.show();
|
||||||
|
@ -650,10 +640,6 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||||
_isControlsLocked = locked;
|
_isControlsLocked = locked;
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun play() {
|
|
||||||
super.play();
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onVideoSizeChanged(videoSize: VideoSize) {
|
override fun onVideoSizeChanged(videoSize: VideoSize) {
|
||||||
gestureControl.resetZoomPan()
|
gestureControl.resetZoomPan()
|
||||||
_lastSourceFit = null;
|
_lastSourceFit = null;
|
||||||
|
@ -723,11 +709,6 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
fun fitHeight(videoSize: VideoSize? = null) {
|
fun fitHeight(videoSize: VideoSize? = null) {
|
||||||
Logger.i(TAG, "Video Fit Height")
|
Logger.i(TAG, "Video Fit Height")
|
||||||
if (_originalBottomMargin != 0) {
|
|
||||||
val layoutParams = _videoView.layoutParams as ConstraintLayout.LayoutParams
|
|
||||||
layoutParams.setMargins(0, 0, 0, _originalBottomMargin)
|
|
||||||
_videoView.layoutParams = layoutParams
|
|
||||||
}
|
|
||||||
|
|
||||||
var h = videoSize?.height ?: lastVideoSource?.height ?: exoPlayer?.player?.videoSize?.height
|
var h = videoSize?.height ?: lastVideoSource?.height ?: exoPlayer?.player?.videoSize?.height
|
||||||
?: 0
|
?: 0
|
||||||
|
@ -768,15 +749,13 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||||
}
|
}
|
||||||
_videoView.resizeMode = _desiredResizeModePortrait
|
_videoView.resizeMode = _desiredResizeModePortrait
|
||||||
|
|
||||||
val marginBottom =
|
|
||||||
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 7f, resources.displayMetrics)
|
|
||||||
val height = TypedValue.applyDimension(
|
val height = TypedValue.applyDimension(
|
||||||
TypedValue.COMPLEX_UNIT_DIP,
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
_lastSourceFit!!,
|
_lastSourceFit!!,
|
||||||
resources.displayMetrics
|
resources.displayMetrics
|
||||||
)
|
)
|
||||||
val rootParams = LayoutParams(LayoutParams.MATCH_PARENT, (height + marginBottom).toInt())
|
_videoView.setPadding(_videoView.paddingLeft, _videoView.paddingTop, _videoView.paddingRight, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 7.0f, Resources.getSystem().displayMetrics).toInt())
|
||||||
rootParams.bottomMargin = marginBottom.toInt()
|
val rootParams = LayoutParams(LayoutParams.MATCH_PARENT, (height).toInt())
|
||||||
_root.layoutParams = rootParams
|
_root.layoutParams = rootParams
|
||||||
isFitMode = true
|
isFitMode = true
|
||||||
}
|
}
|
||||||
|
@ -784,9 +763,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||||
@OptIn(UnstableApi::class)
|
@OptIn(UnstableApi::class)
|
||||||
fun fillHeight(isMiniPlayer: Boolean) {
|
fun fillHeight(isMiniPlayer: Boolean) {
|
||||||
Logger.i(TAG, "Video Fill Height");
|
Logger.i(TAG, "Video Fill Height");
|
||||||
val layoutParams = _videoView.layoutParams as ConstraintLayout.LayoutParams;
|
var layoutParams = _videoView.layoutParams as ConstraintLayout.LayoutParams;
|
||||||
_originalBottomMargin =
|
|
||||||
if (layoutParams.bottomMargin > 0) layoutParams.bottomMargin else _originalBottomMargin;
|
|
||||||
layoutParams.setMargins(0);
|
layoutParams.setMargins(0);
|
||||||
_videoView.layoutParams = layoutParams;
|
_videoView.layoutParams = layoutParams;
|
||||||
_videoView.invalidate();
|
_videoView.invalidate();
|
||||||
|
@ -797,16 +774,14 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
|
||||||
|
|
||||||
if(isMiniPlayer){
|
if(isMiniPlayer){
|
||||||
_videoView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
_videoView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
|
||||||
|
_videoView.setPadding(_videoView.paddingLeft, _videoView.paddingTop, _videoView.paddingRight, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 7.0f, Resources.getSystem().displayMetrics).toInt())
|
||||||
|
} else {
|
||||||
|
_videoView.setPadding(_videoView.paddingLeft, _videoView.paddingTop, _videoView.paddingRight, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.0f, Resources.getSystem().displayMetrics).toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
isFitMode = false;
|
isFitMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Animated Calls
|
|
||||||
fun setEndPadding(value: Float) {
|
|
||||||
setPadding(0, 0, value.toInt(), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateRotateLock() {
|
fun updateRotateLock() {
|
||||||
_control_rotate_lock.visibility = View.VISIBLE;
|
_control_rotate_lock.visibility = View.VISIBLE;
|
||||||
_control_rotate_lock_fullscreen.visibility = View.VISIBLE;
|
_control_rotate_lock_fullscreen.visibility = View.VISIBLE;
|
||||||
|
|
|
@ -54,7 +54,6 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:layout="@layout/fragview_video_detail"
|
tools:layout="@layout/fragview_video_detail"
|
||||||
android:elevation="15dp"
|
|
||||||
android:visibility="invisible" />
|
android:visibility="invisible" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
|
|
|
@ -94,10 +94,13 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp" />
|
android:layout_height="0dp" />
|
||||||
|
|
||||||
|
<!-- TODO: the padding for the recycler view needs to be the same as the minimized video player and perhaps should be dynamic based on whether the player is mini or closed-->
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/list_results"
|
android:id="@+id/list_results"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:paddingBottom="80dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
android:orientation="vertical" />
|
android:orientation="vertical" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<com.futo.platformplayer.views.containers.SingleViewTouchableMotionLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:background="@color/transparent"
|
|
||||||
app:layoutDescription="@xml/videodetail_scene"
|
|
||||||
app:layout_collapseMode="parallax">
|
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
|
||||||
android:id="@+id/touchContainer"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="220dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
android:background="#222222" />
|
|
||||||
|
|
||||||
<com.futo.platformplayer.fragment.mainactivity.main.VideoDetailView
|
|
||||||
android:id="@+id/fragview_videodetail"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/touchContainer"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/touchContainer"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/touchContainer"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/touchContainer"
|
|
||||||
android:nestedScrollingEnabled="false" />
|
|
||||||
|
|
||||||
</com.futo.platformplayer.views.containers.SingleViewTouchableMotionLayout>
|
|
|
@ -1,24 +1,26 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<com.futo.platformplayer.views.containers.CustomMotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:fitsSystemWindows="false"
|
android:fitsSystemWindows="false"
|
||||||
android:background="@drawable/bottom_menu_border"
|
android:background="@color/transparent"
|
||||||
android:id="@+id/videodetail_root"
|
android:id="@+id/videodetail_root"
|
||||||
android:clickable="true">
|
app:layoutDescription="@xml/videodetail_scene">
|
||||||
|
|
||||||
<com.futo.platformplayer.views.behavior.TouchInterceptFrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/layout_player_container"
|
android:id="@+id/layout_player_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp">
|
||||||
android:paddingBottom="6dp"
|
|
||||||
android:elevation="2dp"
|
<!--this acts as a background-->
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
<androidx.cardview.widget.CardView
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
android:layout_width="match_parent"
|
||||||
app:layout_constraintRight_toRightOf="parent">
|
android:layout_height="match_parent"
|
||||||
|
app:cardBackgroundColor="@color/black"
|
||||||
|
android:translationY="-7dp"/>
|
||||||
|
|
||||||
<com.futo.platformplayer.views.video.FutoVideoPlayer
|
<com.futo.platformplayer.views.video.FutoVideoPlayer
|
||||||
android:id="@+id/videodetail_player"
|
android:id="@+id/videodetail_player"
|
||||||
|
@ -38,39 +40,31 @@
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:elevation="4dp"
|
android:elevation="4dp"
|
||||||
android:layout_marginBottom="6dp" />
|
android:layout_marginBottom="6dp" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<androidx.media3.ui.PlayerControlView
|
<androidx.media3.ui.PlayerControlView
|
||||||
android:id="@+id/videodetail_progress"
|
android:id="@+id/videodetail_progress"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="12dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom"
|
app:show_timeout="-1"
|
||||||
android:layout_marginLeft="-6dp"
|
android:elevation="1dp"
|
||||||
android:layout_marginRight="-6dp"
|
android:background="@color/black"
|
||||||
android:layout_marginBottom="6dp"
|
app:controller_layout_id="@layout/video_player_ui_bar" />
|
||||||
app:show_timeout="-1"
|
|
||||||
android:elevation="2dp"
|
|
||||||
app:controller_layout_id="@layout/video_player_ui_bar" />
|
|
||||||
</com.futo.platformplayer.views.behavior.TouchInterceptFrameLayout>
|
|
||||||
|
|
||||||
<!--Minimized Controls-->
|
<!--Minimized Controls-->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/minimize_controls"
|
android:id="@+id/minimize_controls"
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="60dp"
|
android:layout_height="0dp"
|
||||||
|
android:paddingBottom="7dp"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:paddingStart="10dp"
|
android:background="@color/black"
|
||||||
android:clickable="false"
|
android:orientation="horizontal">
|
||||||
android:elevation="5dp"
|
|
||||||
android:alpha="1"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/layout_player_container"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/layout_player_container"
|
|
||||||
app:layout_constraintEnd_toEndOf="@id/layout_player_container"
|
|
||||||
app:layout_constraintWidth_percent="0.7">
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
|
android:layout_marginStart="10dp"
|
||||||
android:layout_marginEnd="10dp"
|
android:layout_marginEnd="10dp"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
<!--Video Title-->
|
<!--Video Title-->
|
||||||
|
@ -180,16 +174,8 @@
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/contentContainer"
|
android:id="@+id/contentContainer"
|
||||||
android:orientation="vertical"
|
android:layout_width="0dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="0dp">
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginTop="-18dp"
|
|
||||||
android:elevation="1dp"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/layout_player_container"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent">
|
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/videodetail_container_main"
|
android:id="@+id/videodetail_container_main"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -394,19 +380,23 @@
|
||||||
android:id="@+id/videodetail_rating"
|
android:id="@+id/videodetail_rating"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/buttons_pins"
|
||||||
android:layout_marginTop="7dp"
|
android:layout_marginTop="7dp"
|
||||||
android:layout_marginStart="15dp" />
|
android:layout_marginStart="15dp"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="spread_inside"/>
|
||||||
|
|
||||||
<com.futo.platformplayer.views.pills.RoundButtonGroup
|
<com.futo.platformplayer.views.pills.RoundButtonGroup
|
||||||
android:id="@+id/buttons_pins"
|
android:id="@+id/buttons_pins"
|
||||||
app:layout_constraintLeft_toRightOf="@id/videodetail_rating"
|
app:layout_constraintStart_toEndOf="@id/videodetail_rating"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:layout_marginLeft="10dp"
|
android:layout_marginStart="10dp"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content" />
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintWidth_max="500dp"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="spread_inside"/>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
|
||||||
|
@ -605,15 +595,13 @@
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/videodetail_quality_overview"
|
android:id="@+id/videodetail_quality_overview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="0dp"
|
||||||
android:elevation="100dp"
|
android:visibility="gone"/>
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/overlay_container"
|
android:id="@+id/overlay_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="0dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="0dp"
|
||||||
android:elevation="100dp"
|
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</com.futo.platformplayer.views.containers.CustomMotionLayout>
|
|
@ -7,8 +7,7 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:layoutDirection="ltr"
|
android:layoutDirection="ltr"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical">
|
||||||
tools:targetApi="28">
|
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/button_minimize"
|
android:id="@+id/button_minimize"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:layoutDirection="ltr"
|
android:layoutDirection="ltr"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
<com.futo.platformplayer.views.behavior.TouchInterceptFrameLayout
|
<com.futo.platformplayer.views.behavior.TouchInterceptFrameLayout
|
||||||
android:id="@+id/layout_bar"
|
android:id="@+id/layout_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="12dp"
|
android:layout_height="wrap_content"
|
||||||
app:shouldInterceptTouches="true"
|
app:shouldInterceptTouches="true"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
|
|
@ -7,14 +7,14 @@
|
||||||
android:background="@color/transparent"
|
android:background="@color/transparent"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<androidx.media3.ui.PlayerView
|
<androidx.media3.ui.PlayerView
|
||||||
|
android:paddingBottom="7dp"
|
||||||
android:id="@+id/video_player"
|
android:id="@+id/video_player"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
app:default_artwork="@drawable/placeholder_video_thumbnail"
|
app:default_artwork="@drawable/placeholder_video_thumbnail"
|
||||||
app:use_artwork="true"
|
app:use_artwork="true"
|
||||||
app:use_controller="false"
|
app:use_controller="false"
|
||||||
app:show_buffering="always"
|
app:show_buffering="always"/>
|
||||||
android:layout_marginBottom="6dp" />
|
|
||||||
<!--
|
<!--
|
||||||
<androidx.media3.ui.PlayerControlView
|
<androidx.media3.ui.PlayerControlView
|
||||||
android:id="@+id/video_player_bar"
|
android:id="@+id/video_player_bar"
|
||||||
|
@ -28,8 +28,7 @@
|
||||||
android:id="@+id/layout_controls_background"
|
android:id="@+id/layout_controls_background"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="#55000000"
|
android:background="#55000000">
|
||||||
android:layout_marginBottom="6dp">
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
|
@ -64,4 +63,4 @@
|
||||||
app:controller_layout_id="@layout/video_player_ui_fullscreen"
|
app:controller_layout_id="@layout/video_player_ui_fullscreen"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -3,208 +3,180 @@
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
|
android:id="@+id/maximize"
|
||||||
app:constraintSetEnd="@id/expanded"
|
app:constraintSetEnd="@id/expanded"
|
||||||
app:constraintSetStart="@id/collapsed"
|
app:constraintSetStart="@id/collapsed"
|
||||||
app:duration="300"
|
app:duration="300">
|
||||||
app:motionInterpolator="easeInOut">
|
|
||||||
|
|
||||||
<OnSwipe
|
<OnSwipe
|
||||||
app:dragDirection="dragUp"
|
app:dragDirection="dragUp"
|
||||||
app:maxAcceleration="200"
|
app:maxAcceleration="200"
|
||||||
app:touchAnchorId="@+id/touchContainer"
|
|
||||||
app:nestedScrollFlags="disableScroll"
|
app:nestedScrollFlags="disableScroll"
|
||||||
|
app:touchAnchorId="@id/layout_player_container"
|
||||||
app:touchAnchorSide="top" />
|
app:touchAnchorSide="top" />
|
||||||
|
|
||||||
<KeyFrameSet>
|
<!--pretty sure this isn't doing anything right now-->
|
||||||
<!--
|
<OnClick
|
||||||
<KeyAttribute
|
app:clickAction="transitionToEnd"
|
||||||
android:alpha="0"
|
app:targetId="@id/layout_player_container" />
|
||||||
app:framePosition="0"
|
|
||||||
app:motionTarget="@id/contentContainer" />
|
|
||||||
|
|
||||||
<KeyAttribute
|
|
||||||
android:alpha="1"
|
|
||||||
app:framePosition="100"
|
|
||||||
app:motionTarget="@id/contentContainer" /> -->
|
|
||||||
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="3"
|
|
||||||
app:motionTarget="@id/touchContainer">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="cardElevation"
|
|
||||||
app:customDimension="0dp" />
|
|
||||||
</KeyAttribute>
|
|
||||||
|
|
||||||
<!--Minimize Progress-->
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="0"
|
|
||||||
app:motionTarget="@id/fragview_videodetail">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="MinimizeProgress"
|
|
||||||
app:customFloatValue="0" />
|
|
||||||
</KeyAttribute>
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="100"
|
|
||||||
app:motionTarget="@id/fragview_videodetail">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="MinimizeProgress"
|
|
||||||
app:customFloatValue="1" />
|
|
||||||
</KeyAttribute>
|
|
||||||
|
|
||||||
<!--Controller Alpha-->
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="0"
|
|
||||||
app:motionTarget="@id/fragview_videodetail">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="ControllerAlpha"
|
|
||||||
app:customFloatValue="0" />
|
|
||||||
</KeyAttribute>
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="100"
|
|
||||||
app:motionTarget="@id/fragview_videodetail">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="ControllerAlpha"
|
|
||||||
app:customFloatValue="1" />
|
|
||||||
</KeyAttribute>
|
|
||||||
|
|
||||||
<!--Content Alpha-->
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="0"
|
|
||||||
app:motionTarget="@id/fragview_videodetail">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="ContentAlpha"
|
|
||||||
app:customFloatValue="0" />
|
|
||||||
</KeyAttribute>
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="30"
|
|
||||||
app:motionTarget="@id/fragview_videodetail">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="ContentAlpha"
|
|
||||||
app:customFloatValue="0" />
|
|
||||||
</KeyAttribute>
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="100"
|
|
||||||
app:motionTarget="@id/fragview_videodetail">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="ContentAlpha"
|
|
||||||
app:customFloatValue="1" />
|
|
||||||
</KeyAttribute>
|
|
||||||
|
|
||||||
<!--MinimizeControlsAlpha Alpha -->
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="0"
|
|
||||||
app:motionTarget="@id/fragview_videodetail">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="MinimizeControlsAlpha"
|
|
||||||
app:customFloatValue="1" />
|
|
||||||
</KeyAttribute>
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="20"
|
|
||||||
app:motionTarget="@id/fragview_videodetail">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="MinimizeControlsAlpha"
|
|
||||||
app:customFloatValue="0" />
|
|
||||||
</KeyAttribute>
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="100"
|
|
||||||
app:motionTarget="@id/fragview_videodetail">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="MinimizeControlsAlpha"
|
|
||||||
app:customFloatValue="0" />
|
|
||||||
</KeyAttribute>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!--Padding Right-->
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="0"
|
|
||||||
app:motionTarget="@id/fragview_videodetail">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="VideoMinimize"
|
|
||||||
app:customFloatValue="1" />
|
|
||||||
</KeyAttribute>
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="20"
|
|
||||||
app:motionTarget="@id/fragview_videodetail">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="VideoMinimize"
|
|
||||||
app:customFloatValue="0" />
|
|
||||||
</KeyAttribute>
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="100"
|
|
||||||
app:motionTarget="@id/fragview_videodetail">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="VideoMinimize"
|
|
||||||
app:customFloatValue="0" />
|
|
||||||
</KeyAttribute>
|
|
||||||
|
|
||||||
<!--Padding Top-->
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="0"
|
|
||||||
app:motionTarget="@id/fragview_videodetail">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="TopPadding"
|
|
||||||
app:customDimension="1dp" />
|
|
||||||
</KeyAttribute>
|
|
||||||
<KeyAttribute
|
|
||||||
app:framePosition="100"
|
|
||||||
app:motionTarget="@id/fragview_videodetail">
|
|
||||||
<CustomAttribute
|
|
||||||
app:attributeName="TopPadding"
|
|
||||||
app:customDimension="0dp" />
|
|
||||||
</KeyAttribute>
|
|
||||||
|
|
||||||
</KeyFrameSet>
|
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<ConstraintSet android:id="@+id/expanded">
|
<Transition
|
||||||
|
android:id="@+id/full_screen"
|
||||||
|
app:constraintSetEnd="@id/full_screen_gesture"
|
||||||
|
app:constraintSetStart="@id/expanded"
|
||||||
|
app:duration="300">
|
||||||
|
|
||||||
<Constraint
|
<OnSwipe
|
||||||
android:id="@id/touchContainer"
|
app:dragDirection="dragUp"
|
||||||
android:layout_width="match_parent"
|
app:maxAcceleration="200"
|
||||||
android:layout_height="220dp"
|
app:nestedScrollFlags="disableScroll"
|
||||||
android:layout_marginStart="0dp"
|
app:onTouchUp="autoCompleteToStart"
|
||||||
android:layout_marginEnd="0dp"
|
app:touchAnchorId="@id/layout_player_container"
|
||||||
android:layout_marginBottom="0dp"
|
app:touchAnchorSide="bottom" />
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
</Transition>
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<Constraint
|
|
||||||
android:id="@id/fragview_videodetail"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginStart="0dp"
|
|
||||||
android:layout_marginEnd="0dp"
|
|
||||||
android:layout_marginBottom="0dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
</ConstraintSet>
|
|
||||||
|
|
||||||
<ConstraintSet android:id="@+id/collapsed">
|
<ConstraintSet android:id="@+id/collapsed">
|
||||||
|
|
||||||
<Constraint
|
<Constraint
|
||||||
android:id="@id/touchContainer"
|
android:id="@id/layout_player_container"
|
||||||
android:layout_height="60dp"
|
android:layout_height="60dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_marginBottom="47dp"
|
||||||
android:layout_marginStart="0dp"
|
android:elevation="3dp"
|
||||||
android:layout_marginEnd="0dp"
|
android:paddingBottom="6dp"
|
||||||
android:layout_marginBottom="48dp"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/minimize_controls"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
|
app:layout_constraintHorizontal_weight="150"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintWidth_max="150dp" />
|
||||||
|
<Constraint
|
||||||
|
android:id="@id/contentContainer"
|
||||||
|
android:elevation="1dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="invisible"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/layout_player_container" />
|
||||||
<Constraint
|
<Constraint
|
||||||
android:id="@id/fragview_videodetail"
|
android:id="@id/minimize_controls"
|
||||||
|
android:layout_width="0dp"
|
||||||
android:layout_height="60dp"
|
android:layout_height="60dp"
|
||||||
android:layout_width="match_parent"
|
android:elevation="1dp"
|
||||||
android:layout_marginStart="0dp"
|
android:visibility="visible"
|
||||||
android:layout_marginEnd="0dp"
|
|
||||||
android:layout_marginBottom="48dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
|
app:layout_constraintHorizontal_weight="350"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/layout_player_container"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/layout_player_container"
|
||||||
|
app:layout_constraintWidth_max="350dp" />
|
||||||
|
<Constraint
|
||||||
|
android:id="@id/videodetail_progress"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="-12dp"
|
||||||
|
android:background="@color/black"
|
||||||
|
android:elevation="2dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/minimize_controls"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/layout_player_container"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/layout_player_container" />
|
||||||
|
<Constraint
|
||||||
|
android:id="@id/videodetail_quality_overview"
|
||||||
|
app:visibilityMode="ignore"/>
|
||||||
|
<Constraint
|
||||||
|
android:id="@id/overlay_container"
|
||||||
|
app:visibilityMode="ignore"/>
|
||||||
</ConstraintSet>
|
</ConstraintSet>
|
||||||
|
|
||||||
</MotionScene>
|
<ConstraintSet android:id="@+id/expanded">
|
||||||
|
<Constraint
|
||||||
|
android:id="@id/layout_player_container"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="0dp"
|
||||||
|
android:elevation="2dp"
|
||||||
|
android:paddingBottom="6dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
<Constraint
|
||||||
|
android:id="@id/contentContainer"
|
||||||
|
android:elevation="2dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/layout_player_container" />
|
||||||
|
<Constraint
|
||||||
|
android:id="@id/minimize_controls"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:elevation="1dp"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/layout_player_container"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/layout_player_container" />
|
||||||
|
<Constraint
|
||||||
|
android:id="@id/videodetail_progress"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="-12dp"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:elevation="1dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/minimize_controls"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/layout_player_container"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/layout_player_container" />
|
||||||
|
<Constraint
|
||||||
|
android:id="@id/videodetail_quality_overview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:elevation="100dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:visibilityMode="ignore"/>
|
||||||
|
<Constraint
|
||||||
|
android:id="@id/overlay_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:elevation="100dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:visibilityMode="ignore"/>
|
||||||
|
</ConstraintSet>
|
||||||
|
|
||||||
|
<ConstraintSet android:id="@+id/full_screen_gesture">
|
||||||
|
<Constraint
|
||||||
|
android:id="@id/layout_player_container"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="-130dp"
|
||||||
|
android:layout_marginBottom="0dp"
|
||||||
|
android:elevation="2dp"
|
||||||
|
android:paddingBottom="6dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
<Constraint
|
||||||
|
android:id="@id/contentContainer"
|
||||||
|
android:elevation="1dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="visible"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/layout_player_container" />
|
||||||
|
<Constraint
|
||||||
|
android:id="@id/minimize_controls"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:elevation="1dp"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/layout_player_container"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/layout_player_container" />
|
||||||
|
<Constraint
|
||||||
|
android:id="@id/videodetail_progress"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="-12dp"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:elevation="1dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/minimize_controls"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/layout_player_container"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/layout_player_container" />
|
||||||
|
</ConstraintSet>
|
||||||
|
</MotionScene>
|
||||||
|
|
1
app/src/stable/assets/sources/apple-podcast
Submodule
1
app/src/stable/assets/sources/apple-podcast
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit f79c7141bcb11464103abc56fd7be492fe8568ab
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 090104c7fa112d9772f43c7c2620e8c2cf3c9d6a
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 13b30fd76e30a60c114c97b876542f7f106b5881
|
Subproject commit ce0571bdeaed4e341351ef477ef4b6599aa4d0fb
|
|
@ -1 +1 @@
|
||||||
Subproject commit 04b4d8ed3163b7146bb58c418c201899e04e34cb
|
Subproject commit 3ee6f3e38a606cf0efba7d9d978e5673af1355f7
|
|
@ -1 +1 @@
|
||||||
Subproject commit 9c835e075c66ea014e544d9fe35fbb317d72a196
|
Subproject commit dae24378823634a9e58533e5c94e26bd3ae70601
|
|
@ -1 +1 @@
|
||||||
Subproject commit 20fd03d9847b308d81ce474144bee79b04385477
|
Subproject commit 86c55522e5a641e64c69238f5c372179ea99e137
|
|
@ -1 +1 @@
|
||||||
Subproject commit a72aeb85d0fc0c17382cb1a7066fe4ec8b63691c
|
Subproject commit ac81711ab8c364f645389717f3f4c59fbf23f856
|
|
@ -1 +1 @@
|
||||||
Subproject commit eb231adeae7acd0ed8b14e2ebc2b93424ac6811c
|
Subproject commit 05e4387318ce710e86c311022c0a97e009241083
|
|
@ -1 +1 @@
|
||||||
Subproject commit 8f8774a782aa49889774920688de371f28317ca6
|
Subproject commit 13a3ae3c7a9fe0ca05ae661e74f7f505a69e057a
|
|
@ -1 +1 @@
|
||||||
Subproject commit 090104c7fa112d9772f43c7c2620e8c2cf3c9d6a
|
Subproject commit f79c7141bcb11464103abc56fd7be492fe8568ab
|
|
@ -1 +1 @@
|
||||||
Subproject commit 13b30fd76e30a60c114c97b876542f7f106b5881
|
Subproject commit ce0571bdeaed4e341351ef477ef4b6599aa4d0fb
|
|
@ -1 +1 @@
|
||||||
Subproject commit 04b4d8ed3163b7146bb58c418c201899e04e34cb
|
Subproject commit 3ee6f3e38a606cf0efba7d9d978e5673af1355f7
|
|
@ -1 +1 @@
|
||||||
Subproject commit 9c835e075c66ea014e544d9fe35fbb317d72a196
|
Subproject commit dae24378823634a9e58533e5c94e26bd3ae70601
|
|
@ -1 +1 @@
|
||||||
Subproject commit 20fd03d9847b308d81ce474144bee79b04385477
|
Subproject commit 86c55522e5a641e64c69238f5c372179ea99e137
|
|
@ -1 +1 @@
|
||||||
Subproject commit a72aeb85d0fc0c17382cb1a7066fe4ec8b63691c
|
Subproject commit ac81711ab8c364f645389717f3f4c59fbf23f856
|
|
@ -1 +1 @@
|
||||||
Subproject commit eb231adeae7acd0ed8b14e2ebc2b93424ac6811c
|
Subproject commit 05e4387318ce710e86c311022c0a97e009241083
|
|
@ -1 +1 @@
|
||||||
Subproject commit 8f8774a782aa49889774920688de371f28317ca6
|
Subproject commit 13a3ae3c7a9fe0ca05ae661e74f7f505a69e057a
|
Loading…
Add table
Add a link
Reference in a new issue