Refactor media views (#866)

* Migrate ImagePagerAdapter to kotlin

* Migrate ViewMediaFragment to kotlin

* Make images and videos share the same activity/pager

* Show descriptions above videos

* Cleanup

* Address code review feedback

* Migrate media fragments to constraint layout
This commit is contained in:
Levi Bard 2018-10-15 19:56:11 +02:00 committed by Konrad Pozniak
commit 952d2a6512
16 changed files with 632 additions and 594 deletions

View file

@ -30,7 +30,6 @@ import android.view.ViewGroup
import android.widget.ImageView
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.ViewMediaActivity
import com.keylesspalace.tusky.ViewVideoActivity
import com.keylesspalace.tusky.di.Injectable
import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.entity.Status
@ -207,7 +206,9 @@ class AccountMediaFragment : BaseFragment(), Injectable {
val type = items[currentIndex].attachment.type
when (type) {
Attachment.Type.IMAGE -> {
Attachment.Type.IMAGE,
Attachment.Type.GIFV,
Attachment.Type.VIDEO -> {
val intent = ViewMediaActivity.newIntent(context, items, currentIndex)
if (view != null && activity != null) {
val url = items[currentIndex].attachment.url
@ -218,13 +219,6 @@ class AccountMediaFragment : BaseFragment(), Injectable {
startActivity(intent)
}
}
Attachment.Type.GIFV, Attachment.Type.VIDEO -> {
val intent = Intent(context, ViewVideoActivity::class.java)
intent.putExtra(ViewVideoActivity.URL_EXTRA, items[currentIndex].attachment.url)
intent.putExtra(ViewVideoActivity.STATUS_ID_EXTRA, items[currentIndex].statusId)
intent.putExtra(ViewVideoActivity.STATUS_URL_EXTRA, items[currentIndex].statusUrl)
startActivity(intent)
}
Attachment.Type.UNKNOWN -> {
}/* Intentionally do nothing. This case is here is to handle when new attachment
* types are added to the API before code is added here to handle them. So, the

View file

@ -36,7 +36,6 @@ import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.ReportActivity;
import com.keylesspalace.tusky.ViewMediaActivity;
import com.keylesspalace.tusky.ViewTagActivity;
import com.keylesspalace.tusky.ViewVideoActivity;
import com.keylesspalace.tusky.db.AccountEntity;
import com.keylesspalace.tusky.db.AccountManager;
import com.keylesspalace.tusky.entity.Attachment;
@ -234,6 +233,8 @@ public abstract class SFragment extends BaseFragment {
final Attachment active = actionable.getAttachments().get(urlIndex);
Attachment.Type type = active.getType();
switch (type) {
case GIFV:
case VIDEO:
case IMAGE: {
final List<AttachmentViewData> attachments = AttachmentViewData.list(actionable);
final Intent intent = ViewMediaActivity.newIntent(getContext(), attachments,
@ -250,15 +251,6 @@ public abstract class SFragment extends BaseFragment {
}
break;
}
case GIFV:
case VIDEO: {
Intent intent = new Intent(getContext(), ViewVideoActivity.class);
intent.putExtra(ViewVideoActivity.URL_EXTRA, active.getUrl());
intent.putExtra(ViewVideoActivity.STATUS_ID_EXTRA, actionable.getId());
intent.putExtra(ViewVideoActivity.STATUS_URL_EXTRA, actionable.getUrl());
startActivity(intent);
break;
}
case UNKNOWN: {
/* Intentionally do nothing. This case is here is to handle when new attachment
* types are added to the API before code is added here to handle them. So, the

View file

@ -0,0 +1,217 @@
/* Copyright 2017 Andrew Dawson
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.fragment
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.content.Context
import android.os.Bundle
import android.support.v4.view.ViewCompat
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.github.chrisbanes.photoview.PhotoViewAttacher
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.ViewMediaActivity
import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show
import com.squareup.picasso.Callback
import com.squareup.picasso.NetworkPolicy
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.activity_view_media.*
import kotlinx.android.synthetic.main.fragment_view_image.*
class ViewImageFragment : ViewMediaFragment() {
interface PhotoActionsListener {
fun onBringUp()
fun onDismiss()
fun onPhotoTap()
}
private lateinit var attacher: PhotoViewAttacher
private lateinit var photoActionsListener: PhotoActionsListener
private lateinit var toolbar: View
private var showingDescription = false
private var isDescriptionVisible = false
companion object {
private const val TAG = "ViewImageFragment"
}
override fun onAttach(context: Context) {
super.onAttach(context)
photoActionsListener = context as PhotoActionsListener
}
override fun setupMediaView(url: String) {
attacher = PhotoViewAttacher(photoView)
// Clicking outside the photo closes the viewer.
attacher.setOnOutsidePhotoTapListener { _ -> photoActionsListener.onDismiss() }
attacher.setOnClickListener { _ -> onMediaTap() }
/* A vertical swipe motion also closes the viewer. This is especially useful when the photo
* mostly fills the screen so clicking outside is difficult. */
attacher.setOnSingleFlingListener { _, _, velocityX, velocityY ->
var result = false
if (Math.abs(velocityY) > Math.abs(velocityX)) {
photoActionsListener.onDismiss()
result = true
}
result
}
// If we are the view to be shown initially...
if (arguments!!.getBoolean(ViewMediaFragment.ARG_START_POSTPONED_TRANSITION)) {
// Try to load image from disk.
Picasso.with(context)
.load(url)
.noFade()
.networkPolicy(NetworkPolicy.OFFLINE)
.into(photoView, object : Callback {
override fun onSuccess() {
// if we loaded image from disk, we should check that view is attached.
if (ViewCompat.isAttachedToWindow(photoView)) {
finishLoadingSuccessfully()
} else {
// if view is not attached yet, wait for an attachment and
// start transition when it's finally ready.
photoView.addOnAttachStateChangeListener(
object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View?) {
finishLoadingSuccessfully()
photoView.removeOnAttachStateChangeListener(this)
}
override fun onViewDetachedFromWindow(v: View?) {}
})
}
}
override fun onError() {
// if there's no image in cache, load from network and start transition
// immediately.
photoActionsListener.onBringUp()
loadImageFromNetwork(url, photoView)
}
})
} else {
// if we're not initial page, don't bother.
loadImageFromNetwork(url, photoView)
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
toolbar = activity!!.toolbar
return inflater.inflate(R.layout.fragment_view_image, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val arguments = this.arguments!!
val attachment = arguments.getParcelable<Attachment>(ARG_ATTACHMENT)
val url: String?
if (attachment != null) {
url = attachment.url
val description = attachment.description
descriptionView.text = description
showingDescription = !TextUtils.isEmpty(description)
isDescriptionVisible = showingDescription
} else {
url = arguments.getString(ARG_AVATAR_URL)
if (url == null) {
throw IllegalArgumentException("attachment or avatar url has to be set")
}
showingDescription = false
isDescriptionVisible = false
}
// Setting visibility without animations so it looks nice when you scroll images
if (showingDescription && (activity as ViewMediaActivity).isToolbarVisible()) {
descriptionView.show()
} else {
descriptionView.hide()
}
setupMediaView(url)
setupToolbarVisibilityListener()
}
private fun onMediaTap() {
photoActionsListener.onPhotoTap()
}
override fun onToolbarVisibilityChange(visible: Boolean) {
if (photoView == null || !userVisibleHint) {
return
}
isDescriptionVisible = showingDescription && visible
val alpha = if (isDescriptionVisible) 1.0f else 0.0f
descriptionView.animate().alpha(alpha)
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
if (isDescriptionVisible) {
descriptionView.show()
} else {
descriptionView.hide()
}
animation.removeListener(this)
}
})
.start()
}
override fun onDetach() {
super.onDetach()
Picasso.with(context).cancelRequest(photoView)
}
private fun loadImageFromNetwork(url: String, photoView: ImageView) {
Picasso.with(context)
.load(url)
.noPlaceholder()
.networkPolicy(NetworkPolicy.NO_STORE)
.into(photoView, object : Callback {
override fun onSuccess() {
finishLoadingSuccessfully()
}
override fun onError() {
progressBar.hide()
}
})
}
private fun finishLoadingSuccessfully() {
progressBar.hide()
attacher.update()
photoActionsListener.onBringUp()
}
}

View file

@ -1,255 +0,0 @@
/* Copyright 2017 Andrew Dawson
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.fragment;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.github.chrisbanes.photoview.PhotoView;
import com.github.chrisbanes.photoview.PhotoViewAttacher;
import com.keylesspalace.tusky.R;
import com.keylesspalace.tusky.ViewMediaActivity;
import com.keylesspalace.tusky.entity.Attachment;
import com.squareup.picasso.Callback;
import com.squareup.picasso.NetworkPolicy;
import com.squareup.picasso.Picasso;
import java.util.Objects;
import kotlin.jvm.functions.Function0;
public final class ViewMediaFragment extends BaseFragment {
public interface PhotoActionsListener {
void onBringUp();
void onDismiss();
void onPhotoTap();
}
private PhotoViewAttacher attacher;
private PhotoActionsListener photoActionsListener;
private View rootView;
private PhotoView photoView;
private TextView descriptionView;
private boolean showingDescription;
private boolean isDescriptionVisible;
private Function0 toolbarVisibiltyDisposable;
private static final String ARG_START_POSTPONED_TRANSITION = "startPostponedTransition";
private static final String ARG_ATTACHMENT = "attach";
private static final String ARG_AVATAR_URL = "avatarUrl";
public static ViewMediaFragment newInstance(@NonNull Attachment attachment,
boolean shouldStartPostponedTransition) {
Bundle arguments = new Bundle(2);
ViewMediaFragment fragment = new ViewMediaFragment();
arguments.putParcelable(ARG_ATTACHMENT, attachment);
arguments.putBoolean(ARG_START_POSTPONED_TRANSITION, shouldStartPostponedTransition);
fragment.setArguments(arguments);
return fragment;
}
public static ViewMediaFragment newAvatarInstance(@NonNull String avatarUrl) {
Bundle arguments = new Bundle(2);
ViewMediaFragment fragment = new ViewMediaFragment();
arguments.putString(ARG_AVATAR_URL, avatarUrl);
arguments.putBoolean(ARG_START_POSTPONED_TRANSITION, true);
fragment.setArguments(arguments);
return fragment;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
photoActionsListener = (PhotoActionsListener) context;
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_view_media, container, false);
photoView = rootView.findViewById(R.id.view_media_image);
descriptionView = rootView.findViewById(R.id.tv_media_description);
final Bundle arguments = Objects.requireNonNull(getArguments(), "Empty arguments");
final Attachment attachment = arguments.getParcelable(ARG_ATTACHMENT);
final String url;
if(attachment != null) {
url = attachment.getUrl();
@Nullable final String description = attachment.getDescription();
descriptionView.setText(description);
showingDescription = !TextUtils.isEmpty(description);
isDescriptionVisible = showingDescription;
} else {
url = arguments.getString(ARG_AVATAR_URL);
if(url == null) {
throw new IllegalArgumentException("attachment or avatar url has to be set");
}
showingDescription = false;
isDescriptionVisible = false;
}
// Setting visibility without animations so it looks nice when you scroll images
//noinspection ConstantConditions
descriptionView.setVisibility(showingDescription
&& (((ViewMediaActivity) getActivity())).isToolbarVisible()
? View.VISIBLE : View.GONE);
attacher = new PhotoViewAttacher(photoView);
// Clicking outside the photo closes the viewer.
attacher.setOnOutsidePhotoTapListener(imageView -> photoActionsListener.onDismiss());
attacher.setOnClickListener(v -> onMediaTap());
/* A vertical swipe motion also closes the viewer. This is especially useful when the photo
* mostly fills the screen so clicking outside is difficult. */
attacher.setOnSingleFlingListener((e1, e2, velocityX, velocityY) -> {
if (Math.abs(velocityY) > Math.abs(velocityX)) {
photoActionsListener.onDismiss();
return true;
}
return false;
});
ViewCompat.setTransitionName(photoView, url);
// If we are the view to be shown initially...
if (arguments.getBoolean(ARG_START_POSTPONED_TRANSITION)) {
// Try to load image from disk.
Picasso.with(getContext())
.load(url)
.noFade()
.networkPolicy(NetworkPolicy.OFFLINE)
.into(photoView, new Callback() {
@Override
public void onSuccess() {
// if we loaded image from disk, we should check that view is attached.
if (ViewCompat.isAttachedToWindow(photoView)) {
finishLoadingSuccessfully();
} else {
// if view is not attached yet, wait for an attachment and
// start transition when it's finally ready.
photoView.addOnAttachStateChangeListener(
new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
finishLoadingSuccessfully();
photoView.removeOnAttachStateChangeListener(this);
}
@Override
public void onViewDetachedFromWindow(View v) {
}
});
}
}
@Override
public void onError() {
// if there's no image in cache, load from network and start transition
// immediately.
photoActionsListener.onBringUp();
loadImageFromNetwork(url, photoView);
}
});
} else {
// if we're not initial page, don't bother.
loadImageFromNetwork(url, photoView);
}
toolbarVisibiltyDisposable = ((ViewMediaActivity) getActivity())
.addToolbarVisibilityListener(this::onToolbarVisibilityChange);
return rootView;
}
@Override
public void onDestroyView() {
if (toolbarVisibiltyDisposable != null) toolbarVisibiltyDisposable.invoke();
super.onDestroyView();
}
private void onMediaTap() {
photoActionsListener.onPhotoTap();
}
private void onToolbarVisibilityChange(boolean visible) {
isDescriptionVisible = showingDescription && visible;
final int visibility = isDescriptionVisible ? View.VISIBLE : View.INVISIBLE;
int alpha = isDescriptionVisible ? 1 : 0;
descriptionView.animate().alpha(alpha)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
descriptionView.setVisibility(visibility);
animation.removeListener(this);
}
})
.start();
}
@Override
public void onDetach() {
super.onDetach();
Picasso.with(getContext())
.cancelRequest(photoView);
}
private void loadImageFromNetwork(String url, ImageView photoView) {
Picasso.with(getContext())
.load(url)
.noPlaceholder()
.networkPolicy(NetworkPolicy.NO_STORE)
.into(photoView, new Callback() {
@Override
public void onSuccess() {
finishLoadingSuccessfully();
}
@Override
public void onError() {
rootView.findViewById(R.id.view_media_progress).setVisibility(View.GONE);
}
});
}
private void finishLoadingSuccessfully() {
rootView.findViewById(R.id.view_media_progress).setVisibility(View.GONE);
attacher.update();
photoActionsListener.onBringUp();
}
}

View file

@ -0,0 +1,75 @@
/* Copyright 2017 Andrew Dawson
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.fragment
import android.os.Bundle
import com.keylesspalace.tusky.ViewMediaActivity
import com.keylesspalace.tusky.entity.Attachment
abstract class ViewMediaFragment : BaseFragment() {
private var toolbarVisibiltyDisposable: Function0<Boolean>? = null
abstract fun setupMediaView(url: String)
abstract fun onToolbarVisibilityChange(visible: Boolean)
companion object {
@JvmStatic protected val ARG_START_POSTPONED_TRANSITION = "startPostponedTransition"
@JvmStatic protected val ARG_ATTACHMENT = "attach"
@JvmStatic protected val ARG_AVATAR_URL = "avatarUrl"
private const val TAG = "ViewMediaFragment"
@JvmStatic
fun newInstance(attachment: Attachment, shouldStartPostponedTransition: Boolean): ViewMediaFragment {
val arguments = Bundle(2)
arguments.putParcelable(ARG_ATTACHMENT, attachment)
arguments.putBoolean(ARG_START_POSTPONED_TRANSITION, shouldStartPostponedTransition)
val fragment = when (attachment.type) {
Attachment.Type.IMAGE -> ViewImageFragment()
Attachment.Type.VIDEO,
Attachment.Type.GIFV -> ViewVideoFragment()
else -> throw Exception("Unknown media type: $attachment")
}
fragment.arguments = arguments
return fragment
}
@JvmStatic
fun newAvatarInstance(avatarUrl: String): ViewMediaFragment {
val arguments = Bundle(2)
val fragment = ViewImageFragment()
arguments.putString(ARG_AVATAR_URL, avatarUrl)
arguments.putBoolean(ARG_START_POSTPONED_TRANSITION, true)
fragment.arguments = arguments
return fragment
}
}
protected fun setupToolbarVisibilityListener() {
toolbarVisibiltyDisposable = (activity as ViewMediaActivity).addToolbarVisibilityListener(object: ViewMediaActivity.ToolbarVisibilityListener {
override fun onToolbarVisiblityChanged(isVisible: Boolean) {
onToolbarVisibilityChange(isVisible)
}
})
}
override fun onDestroyView() {
toolbarVisibiltyDisposable?.invoke()
super.onDestroyView()
}
}

View file

@ -0,0 +1,169 @@
/* Copyright 2017 Andrew Dawson
*
* This file is a part of Tusky.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Tusky; if not,
* see <http://www.gnu.org/licenses>. */
package com.keylesspalace.tusky.fragment
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.annotation.SuppressLint
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.support.v4.view.ViewCompat
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.MediaController
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.ViewMediaActivity
import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show
import kotlinx.android.synthetic.main.activity_view_media.*
import kotlinx.android.synthetic.main.fragment_view_video.*
class ViewVideoFragment : ViewMediaFragment() {
private lateinit var toolbar: View
private val handler = Handler(Looper.getMainLooper())
private val hideToolbar = Runnable {
// Hoist toolbar hiding to activity so it can track state across different fragments
// This is explicitly stored as runnable so that we pass it to the handler later for cancellation
mediaActivity.onPhotoTap()
}
private lateinit var mediaActivity: ViewMediaActivity
private val TOOLBAR_HIDE_DELAY_MS = 3000L
private var showingDescription = false
private var isDescriptionVisible = false
companion object {
private const val TAG = "ViewVideoFragment"
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
// Start/pause/resume video playback as fragment is shown/hidden
super.setUserVisibleHint(isVisibleToUser)
if (videoPlayer == null) {
return
}
if (isVisibleToUser) {
if (mediaActivity.isToolbarVisible()) {
handler.postDelayed(hideToolbar, TOOLBAR_HIDE_DELAY_MS)
}
videoPlayer?.start()
} else {
handler.removeCallbacks(hideToolbar)
videoPlayer?.pause()
}
}
@SuppressLint("ClickableViewAccessibility")
override fun setupMediaView(url: String) {
val videoView = videoPlayer
videoView.setVideoPath(url)
val controller = MediaController(mediaActivity)
controller.setMediaPlayer(videoView)
videoView.setMediaController(controller)
videoView.requestFocus()
videoView.setOnTouchListener { _, _ ->
mediaActivity.onPhotoTap()
false
}
videoView.setOnPreparedListener { mp ->
progressBar.hide()
mp.isLooping = true
if (arguments!!.getBoolean(ViewMediaFragment.ARG_START_POSTPONED_TRANSITION)) {
hideToolbarAfterDelay(TOOLBAR_HIDE_DELAY_MS)
videoView.start()
}
}
if (arguments!!.getBoolean(ViewMediaFragment.ARG_START_POSTPONED_TRANSITION)) {
mediaActivity.onBringUp()
}
}
private fun hideToolbarAfterDelay(delayMilliseconds: Long) {
handler.postDelayed(hideToolbar, delayMilliseconds)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
toolbar = activity!!.toolbar
mediaActivity = activity as ViewMediaActivity
return inflater.inflate(R.layout.fragment_view_video, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val arguments = this.arguments!!
val attachment = arguments.getParcelable<Attachment>(ViewMediaFragment.ARG_ATTACHMENT)
val url: String
if (attachment == null) {
throw IllegalArgumentException("attachment has to be set")
}
url = attachment.url
val description = attachment.description
mediaDescription.text = description
showingDescription = !TextUtils.isEmpty(description)
isDescriptionVisible = showingDescription
// Setting visibility without animations so it looks nice when you scroll media
//noinspection ConstantConditions
if (showingDescription && mediaActivity.isToolbarVisible()) {
mediaDescription.show()
} else {
mediaDescription.hide()
}
ViewCompat.setTransitionName(videoPlayer!!, url)
setupMediaView(url)
setupToolbarVisibilityListener()
}
override fun onToolbarVisibilityChange(visible: Boolean) {
if (videoPlayer == null || !userVisibleHint) {
return
}
isDescriptionVisible = showingDescription && visible
val alpha = if (isDescriptionVisible) 1.0f else 0.0f
mediaDescription.animate().alpha(alpha)
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
if (isDescriptionVisible) {
mediaDescription.show()
} else {
mediaDescription.hide()
}
animation.removeListener(this)
}
})
.start()
if (visible) {
hideToolbarAfterDelay(TOOLBAR_HIDE_DELAY_MS)
} else {
handler.removeCallbacks(hideToolbar)
}
}
}