diff --git a/OCH/mogo-och-bus-passenger/build.gradle b/OCH/mogo-och-bus-passenger/build.gradle index e2c91a3905..24b4c2d2ec 100644 --- a/OCH/mogo-och-bus-passenger/build.gradle +++ b/OCH/mogo-och-bus-passenger/build.gradle @@ -76,6 +76,7 @@ dependencies { implementation project(":OCH:mogo-och-common-module") compileOnly project(":libraries:mogo-map") compileOnly project(':libraries:mogo-adas') + implementation project(':core:mogo-core-res') } diff --git a/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/bean/TaxiPassengerVideoPlay.java b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/bean/TaxiPassengerVideoPlay.java new file mode 100644 index 0000000000..72bbd8e3de --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/bean/TaxiPassengerVideoPlay.java @@ -0,0 +1,38 @@ +package com.mogo.och.bus.passenger.bean; + +public class TaxiPassengerVideoPlay { + + public TaxiPassengerVideoPlay(String url, String imageUrl, String title) { + this.url = url; + this.imageUrl = imageUrl; + this.title = title; + } + + private String url; + private String imageUrl; + private String title; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } +} diff --git a/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/BusPassengerFunctionVideoFragment.kt b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/BusPassengerFunctionVideoFragment.kt index 951906fc49..a5359efed0 100644 --- a/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/BusPassengerFunctionVideoFragment.kt +++ b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/BusPassengerFunctionVideoFragment.kt @@ -1,9 +1,20 @@ package com.mogo.och.bus.passenger.ui import android.os.Bundle +import android.view.View +import androidx.recyclerview.widget.RecyclerView import com.mogo.commons.mvp.MvpFragment import com.mogo.och.bus.passenger.R +import com.mogo.och.bus.passenger.bean.TaxiPassengerVideoPlay import com.mogo.och.bus.passenger.presenter.BusPassengerFunctionVideoPresenter +import com.mogo.och.bus.passenger.ui.adapter.RecyclerVideoAdapter +import com.mogo.och.bus.passenger.ui.layoutmanage.CarouselLayoutManager +import com.mogo.och.bus.passenger.ui.layoutmanage.CarouselZoomPostLayoutListener +import com.mogo.och.bus.passenger.ui.layoutmanage.CenterScrollListener +import com.mogo.och.bus.passenger.view.ConsultVideoPlayer +import com.shuyu.gsyvideoplayer.video.base.GSYVideoView +import kotlinx.android.synthetic.m1.bus_p_function_video_fragment.* +import kotlin.math.floor /** * @author: yangyakun @@ -11,6 +22,9 @@ import com.mogo.och.bus.passenger.presenter.BusPassengerFunctionVideoPresenter */ class BusPassengerFunctionVideoFragment : MvpFragment() { + + private val arrayListOf = ArrayList() + override fun getLayoutId(): Int { return R.layout.bus_p_function_video_fragment } @@ -20,7 +34,108 @@ class BusPassengerFunctionVideoFragment : } override fun initViews() { + initConsultData() + val carouselLayoutManager = CarouselLayoutManager(CarouselLayoutManager.HORIZONTAL, true) + carouselLayoutManager.setPostLayoutListener(CarouselZoomPostLayoutListener()) + carouselLayoutManager.maxVisibleItems = 1 + rvVideoPlaylist.addOnScrollListener(object : CenterScrollListener() { + var prePlayerPosition = 0 + override fun pageSelect(recyclerView: RecyclerView?, newState: Int) { + //播放视频 + val (centerItemPosition: kotlin.Int, player) = getPlayer(carouselLayoutManager) + if (player is ConsultVideoPlayer) { + if (prePlayerPosition != centerItemPosition) { + if (player.currentState == GSYVideoView.CURRENT_STATE_PAUSE) { + player.onVideoReset() + } + val playerHolder = + carouselLayoutManager.findViewByPosition(prePlayerPosition) + val prePlayer = + playerHolder?.findViewById(R.id.video_item_player) + prePlayer?.onVideoReset() + val taxiPassengerVideoPlay = arrayListOf[centerItemPosition] + setBackageAndPlayNext(taxiPassengerVideoPlay) + } else { + player.onVideoResume(false) + } + } + prePlayerPosition = centerItemPosition + } + override fun pageStop() { + val (_: kotlin.Int, player) = getPlayer(carouselLayoutManager) + if (player is ConsultVideoPlayer) { + player.onVideoPause() + } + } + + }) + carouselLayoutManager.addOnDargAutoDiffListener { adapterPosition, currentPosition -> + val fl = adapterPosition - floor(adapterPosition) + var currentIndex = currentPosition + if (fl > 0.5) { + if (currentPosition == 0) { + currentIndex = rvVideoPlaylist?.adapter!!.itemCount - 1 + } else { + currentIndex -= 1 + } + } + } + val recyclerVideoAdapter = RecyclerVideoAdapter(requireContext(), arrayListOf, rvVideoPlaylist) + recyclerVideoAdapter.setOnThumbImageClilckListener { + val (_: kotlin.Int, player) = getPlayer(carouselLayoutManager) + if (player is ConsultVideoPlayer) { + player.onVideoReset() + player.thumbImageViewLayout.visibility = View.VISIBLE + } + rvVideoPlaylist?.smoothScrollToPosition(it) + } + rvVideoPlaylist?.layoutManager = carouselLayoutManager + rvVideoPlaylist?.setHasFixedSize(true) + rvVideoPlaylist?.adapter = recyclerVideoAdapter + } + + private fun getPlayer(carouselLayoutManager: CarouselLayoutManager): Pair { + val centerItemPosition: Int = carouselLayoutManager.centerItemPosition + val playerHolder = carouselLayoutManager.findViewByPosition(centerItemPosition) + val player = playerHolder?.findViewById(R.id.video_item_player) + return Pair(centerItemPosition, player) + } + + private fun setBackageAndPlayNext(taxiPassengerVideoPlay: TaxiPassengerVideoPlay) { + // 设置背景图片 + } + + private fun initConsultData() { + arrayListOf.clear() + arrayListOf.add( + TaxiPassengerVideoPlay( + "https://img.zhidaohulian.com/fileServer/online_car_hailing/1655708596763/全车型混剪增加红旗车队.m4v", + "https://img.zhidaohulian.com/fileServer/online_car_hailing/1655969511280/车队.png", + "蘑菇车联覆盖生活的方方面面" + ) + ) + arrayListOf.add( + TaxiPassengerVideoPlay( + "https://img.zhidaohulian.com/fileServer/online_car_hailing/1655708554279/红旗车队.m4v", + "https://img.zhidaohulian.com/fileServer/online_car_hailing/1655969553174/红旗重新排版.png", + "蘑菇车联之红旗车队" + ) + ) + arrayListOf.add( + TaxiPassengerVideoPlay( + "https://img.zhidaohulian.com/fileServer/online_car_hailing/1655708499497/大运会合作解说版.m4v", + "https://img.zhidaohulian.com/fileServer/online_car_hailing/1655969536177/大运会.png", + "蘑菇车联牵手成都大运会" + ) + ) + arrayListOf.add( + TaxiPassengerVideoPlay( + "https://img.zhidaohulian.com/fileServer/online_car_hailing/1655708409810/20210610重新排版3屏.m4v", + "https://img.zhidaohulian.com/fileServer/online_car_hailing/1655969579713/三屏.png", + "多视角体验蘑菇车联自动驾驶" + ) + ) } override fun createPresenter(): BusPassengerFunctionVideoPresenter { diff --git a/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/adapter/RecyclerItemVideoHolder.java b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/adapter/RecyclerItemVideoHolder.java new file mode 100644 index 0000000000..8092e7b046 --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/adapter/RecyclerItemVideoHolder.java @@ -0,0 +1,29 @@ +package com.mogo.och.bus.passenger.ui.adapter; + +import android.content.Context; +import android.view.View; + +import androidx.recyclerview.widget.RecyclerView; + +import com.mogo.och.bus.passenger.R; +import com.mogo.och.bus.passenger.view.ConsultVideoPlayer; +import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder; + +public class RecyclerItemVideoHolder extends RecyclerView.ViewHolder { + + public final static String TAG = "RecyclerView2List"; + + protected Context context; + + public ConsultVideoPlayer gsyVideoPlayer; + + GSYVideoOptionBuilder gsyVideoOptionBuilder; + + public RecyclerItemVideoHolder(Context context, View v) { + super(v); + this.context = context; + gsyVideoPlayer = v.findViewById(R.id.video_item_player); + gsyVideoOptionBuilder = new GSYVideoOptionBuilder(); + } + +} diff --git a/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/adapter/RecyclerVideoAdapter.java b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/adapter/RecyclerVideoAdapter.java new file mode 100644 index 0000000000..5826d72403 --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/adapter/RecyclerVideoAdapter.java @@ -0,0 +1,130 @@ +package com.mogo.och.bus.passenger.ui.adapter; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; +import com.mogo.eagle.core.utilcode.util.ToastUtils; +import com.mogo.och.bus.passenger.R; +import com.mogo.och.bus.passenger.bean.TaxiPassengerVideoPlay; +import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack; + +import java.util.List; + +import me.jessyan.autosize.AutoSizeCompat; + +public class RecyclerVideoAdapter extends RecyclerView.Adapter { + private final static String TAG = "RecyclerBaseAdapter"; + + private List itemDataList = null; + private Context context = null; + private OnThumbImageClilckListener onThumbImageClilckListener; + private RecyclerView recyclerView; + + public OnThumbImageClilckListener getOnThumbImageClilckListener() { + return onThumbImageClilckListener; + } + + public void setOnThumbImageClilckListener(OnThumbImageClilckListener onThumbImageClilckListener) { + this.onThumbImageClilckListener = onThumbImageClilckListener; + } + + public RecyclerVideoAdapter(Context context, List itemDataList,RecyclerView recyclerView) { + this.itemDataList = itemDataList; + this.context = context; + this.recyclerView = recyclerView; + } + + public TaxiPassengerVideoPlay getItemByPosition(int position){ + if(itemDataList!=null){ + return itemDataList.get(position); + } + return null; + } + + @NonNull + @Override + public RecyclerItemVideoHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View v = LayoutInflater.from(context).inflate(R.layout.list_video_item_normal, parent, false); + RecyclerItemVideoHolder recyclerItemVideoHolder = new RecyclerItemVideoHolder(context, v); + recyclerItemVideoHolder.setIsRecyclable(false); + return recyclerItemVideoHolder; + } + + + + @Override + public void onBindViewHolder(@NonNull final RecyclerItemVideoHolder holder, int position) { + final TaxiPassengerVideoPlay taxiPassengerVideoPlay = itemDataList.get(position); + AutoSizeCompat.autoConvertDensityOfGlobal(holder.itemView.getResources()); + holder.gsyVideoOptionBuilder + .setEnlargeImageRes(R.drawable.taxi_p_change_full) + .setUrl(taxiPassengerVideoPlay.getUrl()) + .setCacheWithPlay(true) + .setPlayTag(taxiPassengerVideoPlay.getImageUrl()+position) + .setThumbPlay(false) + .build(holder.gsyVideoPlayer); + holder.gsyVideoPlayer.getTitleTextView().setText(taxiPassengerVideoPlay.getTitle()); + Glide.with(context) + .load(taxiPassengerVideoPlay.getImageUrl()) + .apply(new RequestOptions().placeholder(R.drawable.taxi_p_video_holder).centerCrop()) + .into(holder.gsyVideoPlayer.coverImage); + holder.gsyVideoPlayer.getThumbImageViewLayout().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if(onThumbImageClilckListener!=null){ + onThumbImageClilckListener.onDxChanged(holder.getAbsoluteAdapterPosition()); + } + } + }); + holder.gsyVideoPlayer.setVideoAllCallBack(new GSYSampleCallBack(){ + @Override + public void onAutoComplete(String url, Object... objects) { + holder.gsyVideoPlayer.onVideoReset(); + if(holder.getAbsoluteAdapterPosition()==getItemCount()-1){ + recyclerView.smoothScrollToPosition(0); + }else { + recyclerView.smoothScrollToPosition(holder.getAbsoluteAdapterPosition()+1); + } + } + + @Override + public void onClickBlank(String url, Object... objects) { + super.onClickBlank(url, objects); + recyclerView.smoothScrollToPosition(holder.getAbsoluteAdapterPosition()); + } + + @Override + public void onPlayError(String url, Object... objects) { + ToastUtils.showLong("哎呀,出错了,看看其他视频吧"); + } + + @Override + public void onClickStartError(String url, Object... objects) { + ToastUtils.showLong("哎呀,出错了,看看其他视频吧"); + } + }); + } + + @Override + public int getItemCount() { + return itemDataList.size(); + } + + + @Override + public int getItemViewType(int position) { + return 1; + } + + + public interface OnThumbImageClilckListener { + void onDxChanged(int targetPosition); + } +} diff --git a/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/layoutmanage/CarouselLayoutManager.java b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/layoutmanage/CarouselLayoutManager.java new file mode 100644 index 0000000000..c82d57dac4 --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/layoutmanage/CarouselLayoutManager.java @@ -0,0 +1,970 @@ +package com.mogo.och.bus.passenger.ui.layoutmanage; + +import android.graphics.PointF; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.CallSuper; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.LinearSmoothScroller; +import androidx.recyclerview.widget.OrientationHelper; +import androidx.recyclerview.widget.RecyclerView; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * An implementation of {@link RecyclerView.LayoutManager} that layout items like carousel. + * Generally there is one center item and bellow this item there are maximum {@link CarouselLayoutManager#getMaxVisibleItems()} items on each side of the center + * item. By default {@link CarouselLayoutManager#getMaxVisibleItems()} is {@link CarouselLayoutManager#MAX_VISIBLE_ITEMS}.
+ *
+ * This LayoutManager supports only fixedSized adapter items.
+ *
+ * This LayoutManager supports {@link CarouselLayoutManager#HORIZONTAL} and {@link CarouselLayoutManager#VERTICAL} orientations.
+ *
+ * This LayoutManager supports circle layout. By default it if disabled. We don't recommend to use circle layout with adapter items count less then 3.
+ *
+ * Please be sure that layout_width of adapter item is a constant value and not {@link ViewGroup.LayoutParams#MATCH_PARENT} + * for {@link #HORIZONTAL} orientation. + * So like layout_height is not {@link ViewGroup.LayoutParams#MATCH_PARENT} for {@link CarouselLayoutManager#VERTICAL}
+ *
+ */ +public class CarouselLayoutManager extends RecyclerView.LayoutManager implements RecyclerView.SmoothScroller.ScrollVectorProvider { + + public static final int HORIZONTAL = OrientationHelper.HORIZONTAL; + public static final int VERTICAL = OrientationHelper.VERTICAL; + /** + * 固定值一直不变 + */ + public static final int INVALID_POSITION = -1; + public static final int MAX_VISIBLE_ITEMS = 3; + + private static final boolean CIRCLE_LAYOUT = false; + + private boolean mDecoratedChildSizeInvalid; + private Integer mDecoratedChildWidth; + private Integer mDecoratedChildHeight; + + private final int mOrientation; + private boolean mCircleLayout; + + private int mPendingScrollPosition; + + private final LayoutHelper mLayoutHelper = new LayoutHelper(MAX_VISIBLE_ITEMS); + + private PostLayoutListener mViewPostLayout; + + private final List mOnCenterItemSelectionListeners = new ArrayList<>(); + private final List onDargAutoDiffListeners = new ArrayList<>(); + private int mCenterItemPosition = INVALID_POSITION; + private int mItemsCount; + + @Nullable + private CarouselSavedState mPendingCarouselSavedState; + + /** + * @param orientation should be {@link #VERTICAL} or {@link #HORIZONTAL} + */ + @SuppressWarnings("unused") + public CarouselLayoutManager(final int orientation) { + this(orientation, CIRCLE_LAYOUT); + } + + /** + * If circleLayout is true then all items will be in cycle. Scroll will be infinite on both sides. + * + * @param orientation should be {@link #VERTICAL} or {@link #HORIZONTAL} + * @param circleLayout true for enabling circleLayout + */ + @SuppressWarnings("unused") + public CarouselLayoutManager(final int orientation, final boolean circleLayout) { + if (HORIZONTAL != orientation && VERTICAL != orientation) { + throw new IllegalArgumentException("orientation should be HORIZONTAL or VERTICAL"); + } + mOrientation = orientation; + mCircleLayout = circleLayout; + mPendingScrollPosition = INVALID_POSITION; + } + + /** + * Change circle layout type + */ + @SuppressWarnings("unused") + public void setCircleLayout(final boolean circleLayout) { + if (mCircleLayout != circleLayout) { + mCircleLayout = circleLayout; + requestLayout(); + } + } + + /** + * Setup {@link PostLayoutListener} for this LayoutManager. + * Its methods will be called for each visible view item after general LayoutManager layout finishes.
+ *
+ * Generally this method should be used for scaling and translating view item for better (different) view presentation of layouting. + * + * @param postLayoutListener listener for item layout changes. Can be null. + */ + @SuppressWarnings("unused") + public void setPostLayoutListener(@Nullable final PostLayoutListener postLayoutListener) { + mViewPostLayout = postLayoutListener; + requestLayout(); + } + + /** + * Setup maximum visible (layout) items on each side of the center item. + * Basically during scrolling there can be more visible items (+1 item on each side), but in idle state this is the only reached maximum. + * + * @param maxVisibleItems should be great then 0, if bot an {@link IllegalAccessException} will be thrown + */ + @CallSuper + @SuppressWarnings("unused") + public void setMaxVisibleItems(final int maxVisibleItems) { + if (0 > maxVisibleItems) { + throw new IllegalArgumentException("maxVisibleItems can't be less then 0"); + } + mLayoutHelper.mMaxVisibleItems = maxVisibleItems; + requestLayout(); + } + + /** + * @return current setup for maximum visible items. + * @see #setMaxVisibleItems(int) + */ + @SuppressWarnings("unused") + public int getMaxVisibleItems() { + return mLayoutHelper.mMaxVisibleItems; + } + + @Override + public RecyclerView.LayoutParams generateDefaultLayoutParams() { + return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + } + + /** + * @return current layout orientation + * @see #VERTICAL + * @see #HORIZONTAL + */ + public int getOrientation() { + return mOrientation; + } + + @Override + public boolean canScrollHorizontally() { + return 0 != getChildCount() && HORIZONTAL == mOrientation; + } + + @Override + public boolean canScrollVertically() { + return 0 != getChildCount() && VERTICAL == mOrientation; + } + + /** + * @return current layout center item + */ + public int getCenterItemPosition() { + return mCenterItemPosition; + } + + /** + * @param onCenterItemSelectionListener listener that will trigger when ItemSelectionChanges. can't be null + */ + public void addOnItemSelectionListener(@NonNull final OnCenterItemSelectionListener onCenterItemSelectionListener) { + mOnCenterItemSelectionListeners.add(onCenterItemSelectionListener); + } + + /** + * @param onCenterItemSelectionListener listener that was previously added by {@link #addOnItemSelectionListener(OnCenterItemSelectionListener)} + */ + public void removeOnItemSelectionListener(@NonNull final OnCenterItemSelectionListener onCenterItemSelectionListener) { + mOnCenterItemSelectionListeners.remove(onCenterItemSelectionListener); + } + + public void addOnDargAutoDiffListener(@NonNull final OnDargAutoDiffListener onDargAutoDiffListener) { + onDargAutoDiffListeners.add(onDargAutoDiffListener); + } + + public void removeOnDargAutoDiffListener(@NonNull final OnDargAutoDiffListener onDargAutoDiffListener) { + onDargAutoDiffListeners.remove(onDargAutoDiffListener); + } + + @SuppressWarnings("RefusedBequest") + @Override + public void scrollToPosition(final int position) { + if (0 > position) { + throw new IllegalArgumentException("position can't be less then 0. position is : " + position); + } + mPendingScrollPosition = position; + requestLayout(); + } + + @SuppressWarnings("RefusedBequest") + @Override + public void smoothScrollToPosition(@NonNull final RecyclerView recyclerView, @NonNull final RecyclerView.State state, final int position) { + final LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) { + @Override + public int calculateDyToMakeVisible(final View view, final int snapPreference) { + if (!canScrollVertically()) { + return 0; + } + + return getOffsetForCurrentView(view); + } + + @Override + public int calculateDxToMakeVisible(final View view, final int snapPreference) { + if (!canScrollHorizontally()) { + return 0; + } + return getOffsetForCurrentView(view); + } + }; + linearSmoothScroller.setTargetPosition(position); + startSmoothScroll(linearSmoothScroller); + } + + @Override + @Nullable + public PointF computeScrollVectorForPosition(final int targetPosition) { + if (0 == getChildCount()) { + return null; + } + final float directionDistance = getScrollDirection(targetPosition); + //noinspection NumericCastThatLosesPrecision + final int direction = (int) -Math.signum(directionDistance); + + if (HORIZONTAL == mOrientation) { + return new PointF(direction, 0); + } else { + return new PointF(0, direction); + } + } + + private float getScrollDirection(final int targetPosition) { + final float currentScrollPosition = makeScrollPositionInRange0ToCount(getCurrentScrollPosition(), mItemsCount); + + if (mCircleLayout) { + final float t1 = currentScrollPosition - targetPosition; + final float t2 = Math.abs(t1) - mItemsCount; + if (Math.abs(t1) > Math.abs(t2)) { + return Math.signum(t1) * t2; + } else { + return t1; + } + } else { + return currentScrollPosition - targetPosition; + } + } + + @Override + public int scrollVerticallyBy(final int dy, @NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) { + if (HORIZONTAL == mOrientation) { + return 0; + } + return scrollBy(dy, recycler, state); + } + + @Override + public int scrollHorizontallyBy(final int dx, final RecyclerView.Recycler recycler, final RecyclerView.State state) { + if (VERTICAL == mOrientation) { + return 0; + } + return scrollBy(dx, recycler, state); + } + + /** + * This method is called from {@link #scrollHorizontallyBy(int, RecyclerView.Recycler, RecyclerView.State)} and + * {@link #scrollVerticallyBy(int, RecyclerView.Recycler, RecyclerView.State)} to calculate needed scroll that is allowed.
+ *
+ * This method may do relayout work. + * + * @param diff 要滚动的距离 + * @param recycler 回收期 + * @param state Transient state of RecyclerView + * @return distance that we actually scrolled by + */ + @CallSuper + protected int scrollBy(final int diff, @NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) { + if (null == mDecoratedChildWidth || null == mDecoratedChildHeight) { + return 0; + } + if (0 == getChildCount() || 0 == diff) { + return 0; + } + final int resultScroll; + if (mCircleLayout) { + resultScroll = diff; + + mLayoutHelper.mScrollOffset += resultScroll; + + final int maxOffset = getScrollItemSize() * mItemsCount; + while (0 > mLayoutHelper.mScrollOffset) { + mLayoutHelper.mScrollOffset += maxOffset; + } + while (mLayoutHelper.mScrollOffset > maxOffset) { + mLayoutHelper.mScrollOffset -= maxOffset; + } + + mLayoutHelper.mScrollOffset -= resultScroll; + } else { + final int maxOffset = getMaxScrollOffset(); + + if (0 > mLayoutHelper.mScrollOffset + diff) { + resultScroll = -mLayoutHelper.mScrollOffset; //to make it 0 + } else if (mLayoutHelper.mScrollOffset + diff > maxOffset) { + resultScroll = maxOffset - mLayoutHelper.mScrollOffset; //to make it maxOffset + } else { + resultScroll = diff; + } + } + if (0 != resultScroll) { + mLayoutHelper.mScrollOffset += resultScroll; + fillData(recycler, state); + } + return resultScroll; + } + + @Override + public void onMeasure(@NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state, final int widthSpec, final int heightSpec) { + mDecoratedChildSizeInvalid = true; + + super.onMeasure(recycler, state, widthSpec, heightSpec); + } + + @SuppressWarnings("rawtypes") + @Override + public void onAdapterChanged(final RecyclerView.Adapter oldAdapter, final RecyclerView.Adapter newAdapter) { + super.onAdapterChanged(oldAdapter, newAdapter); + + removeAllViews(); + } + + + @SuppressWarnings("RefusedBequest") + @Override + @CallSuper + public void onLayoutChildren(@NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) { + if (0 == state.getItemCount()) { + removeAndRecycleAllViews(recycler); + selectItemCenterPosition(INVALID_POSITION); + return; + } + + detachAndScrapAttachedViews(recycler); + + if (null == mDecoratedChildWidth || mDecoratedChildSizeInvalid) { + final List scrapList = recycler.getScrapList(); + + final boolean shouldRecycle; + final View view; + if (scrapList.isEmpty()) { + shouldRecycle = true; + final int itemsCount = state.getItemCount(); + view = recycler.getViewForPosition( + mPendingScrollPosition == INVALID_POSITION ? + 0 : + Math.max(0, Math.min(itemsCount - 1, mPendingScrollPosition)) + ); + addView(view); + } else { + shouldRecycle = false; + view = scrapList.get(0).itemView; + } + measureChildWithMargins(view, 0, 0); + + final int decoratedChildWidth = getDecoratedMeasuredWidth(view); + final int decoratedChildHeight = getDecoratedMeasuredHeight(view); + if (shouldRecycle) { + detachAndScrapView(view, recycler); + } + + if (null != mDecoratedChildWidth && (mDecoratedChildWidth != decoratedChildWidth || mDecoratedChildHeight != decoratedChildHeight)) { + if (INVALID_POSITION == mPendingScrollPosition && null == mPendingCarouselSavedState) { + mPendingScrollPosition = mCenterItemPosition; + } + } + + mDecoratedChildWidth = decoratedChildWidth; + mDecoratedChildHeight = decoratedChildHeight; + mDecoratedChildSizeInvalid = false; + } + + if (INVALID_POSITION != mPendingScrollPosition) { + final int itemsCount = state.getItemCount(); + mPendingScrollPosition = 0 == itemsCount ? INVALID_POSITION : Math.max(0, Math.min(itemsCount - 1, mPendingScrollPosition)); + } + if (INVALID_POSITION != mPendingScrollPosition) { + mLayoutHelper.mScrollOffset = calculateScrollForSelectingPosition(mPendingScrollPosition, state); + mPendingScrollPosition = INVALID_POSITION; + mPendingCarouselSavedState = null; + } else if (null != mPendingCarouselSavedState) { + mLayoutHelper.mScrollOffset = calculateScrollForSelectingPosition(mPendingCarouselSavedState.mCenterItemPosition, state); + mPendingCarouselSavedState = null; + } else if (state.didStructureChange() && INVALID_POSITION != mCenterItemPosition) { + mLayoutHelper.mScrollOffset = calculateScrollForSelectingPosition(mCenterItemPosition, state); + } + + fillData(recycler, state); + } + + private int calculateScrollForSelectingPosition(final int itemPosition, final RecyclerView.State state) { + if (itemPosition == INVALID_POSITION) { + return 0; + } + + final int fixedItemPosition = itemPosition < state.getItemCount() ? itemPosition : state.getItemCount() - 1; + return fixedItemPosition * (VERTICAL == mOrientation ? mDecoratedChildHeight : mDecoratedChildWidth); + } + + private void fillData(@NonNull final RecyclerView.Recycler recycler, @NonNull final RecyclerView.State state) { + final float currentScrollPosition = getCurrentScrollPosition(); + + generateLayoutOrder(currentScrollPosition, state); + detachAndScrapAttachedViews(recycler); + recyclerOldViews(recycler); + + final int width = getWidthNoPadding(); + final int height = getHeightNoPadding(); + if (VERTICAL == mOrientation) { + fillDataVertical(recycler, width, height); + } else { + fillDataHorizontal(recycler, width, height); + } + + recycler.clear(); + + detectOnItemSelectionChanged(currentScrollPosition, state); + } + + private void detectOnItemSelectionChanged(final float currentScrollPosition, final RecyclerView.State state) { + final float absCurrentScrollPosition = makeScrollPositionInRange0ToCount(currentScrollPosition, state.getItemCount()); + final int centerItem = Math.round(absCurrentScrollPosition); + if(currentScrollPosition-centerItem!=0){ + new Handler(Looper.getMainLooper()).post(() -> dragDxDiff(currentScrollPosition,mCenterItemPosition)); + } + if (mCenterItemPosition != centerItem) { + mCenterItemPosition = centerItem; + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + selectItemCenterPosition(centerItem); + } + }); + } + } + + private void selectItemCenterPosition(final int centerItem) { + for (final OnCenterItemSelectionListener onCenterItemSelectionListener : mOnCenterItemSelectionListeners) { + onCenterItemSelectionListener.onCenterItemChanged(centerItem); + } + } + private void dragDxDiff(final float centerItem,final int currentPosition) { + for (final OnDargAutoDiffListener onDargAutoDiffListener : onDargAutoDiffListeners) { + onDargAutoDiffListener.onDxChanged(centerItem,currentPosition); + } + } + + private void fillDataVertical(final RecyclerView.Recycler recycler, final int width, final int height) { + final int start = (width - mDecoratedChildWidth) / 2; + final int end = start + mDecoratedChildWidth; + + final int centerViewTop = (height - mDecoratedChildHeight) / 2; + + for (int i = 0, count = mLayoutHelper.mLayoutOrder.length; i < count; ++i) { + final LayoutOrder layoutOrder = mLayoutHelper.mLayoutOrder[i]; + final int offset = getCardOffsetByPositionDiff(layoutOrder.mItemPositionDiff); + final int top = centerViewTop + offset; + final int bottom = top + mDecoratedChildHeight; + fillChildItem(start, top, end, bottom, layoutOrder, recycler, i); + } + } + + private void fillDataHorizontal(final RecyclerView.Recycler recycler, final int width, final int height) { + final int top = (height - mDecoratedChildHeight) / 2; + final int bottom = top + mDecoratedChildHeight; + + final int centerViewStart = (width - mDecoratedChildWidth) / 2; + + for (int i = 0, count = mLayoutHelper.mLayoutOrder.length; i < count; ++i) { + final LayoutOrder layoutOrder = mLayoutHelper.mLayoutOrder[i]; + final int offset = getCardOffsetByPositionDiff(layoutOrder.mItemPositionDiff); + final int start = centerViewStart + offset; + final int end = start + mDecoratedChildWidth; + fillChildItem(start, top, end, bottom, layoutOrder, recycler, i); + } + } + + + @SuppressWarnings("MethodWithTooManyParameters") + private void fillChildItem(final int start, final int top, final int end, final int bottom, @NonNull final LayoutOrder layoutOrder, @NonNull final RecyclerView.Recycler recycler, final int i) { + final View view = bindChild(layoutOrder.mItemAdapterPosition, recycler); + ViewCompat.setElevation(view, i); + ItemTransformation transformation = null; + if (null != mViewPostLayout) { + transformation = mViewPostLayout.transformChild(view, layoutOrder.mItemPositionDiff, mOrientation, layoutOrder.mItemAdapterPosition); + } + if (null == transformation) { + view.layout(start, top, end, bottom); + } else { + view.layout(Math.round(start + transformation.mTranslationX), Math.round(top + transformation.mTranslationY), + Math.round(end + transformation.mTranslationX), Math.round(bottom + transformation.mTranslationY)); + view.setScaleX(transformation.mScaleX); + view.setScaleY(transformation.mScaleY); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + view.setTransitionAlpha(transformation.mAlpha); + }else { + view.setAlpha(transformation.mAlpha); + } + } + } + + /** + * 中心项目的当前滚动位置。如果是循环布局,则该值可以在任何范围内。如果不是,那么它在[0,-1] + */ + private float getCurrentScrollPosition() { + final int fullScrollSize = getMaxScrollOffset(); + if (0 == fullScrollSize) { + return 0; + } + return 1.0f * mLayoutHelper.mScrollOffset / getScrollItemSize(); + } + + /** + * 填充布局中所有项目的最大滚动值。通常,这仅适用于非循环布局。 + */ + private int getMaxScrollOffset() { + return getScrollItemSize() * (mItemsCount - 1); + } + + /** + * Because we can support old Android versions, we should layout our children in specific order to make our center view in the top of layout + * (this item should layout last). So this method will calculate layout order and fill up {@link #mLayoutHelper} object. + * This object will be filled by only needed to layout items. Non visible items will not be there. + * + * @param currentScrollPosition current scroll position this is a value that indicates position of center item + * (if this value is int, then center item is really in the center of the layout, else it is near state). + * Be aware that this value can be in any range is it is cycle layout + * @param state Transient state of RecyclerView + * @see #getCurrentScrollPosition() + */ + private void generateLayoutOrder(final float currentScrollPosition, @NonNull final RecyclerView.State state) { + mItemsCount = state.getItemCount(); + final float absCurrentScrollPosition = makeScrollPositionInRange0ToCount(currentScrollPosition, mItemsCount); + final int centerItem = Math.round(absCurrentScrollPosition); + + if (mCircleLayout && 1 < mItemsCount) { + final int layoutCount = Math.min(mLayoutHelper.mMaxVisibleItems * 2 + 1, mItemsCount); + + mLayoutHelper.initLayoutOrder(layoutCount); + + final int countLayoutHalf = layoutCount / 2; + // before center item + for (int i = 1; i <= countLayoutHalf; ++i) { + final int position = Math.round(absCurrentScrollPosition - i + mItemsCount) % mItemsCount; + mLayoutHelper.setLayoutOrder(countLayoutHalf - i, position, centerItem - absCurrentScrollPosition - i); + } + // after center item + for (int i = layoutCount - 1; i >= countLayoutHalf + 1; --i) { + final int position = Math.round(absCurrentScrollPosition - i + layoutCount) % mItemsCount; + mLayoutHelper.setLayoutOrder(i - 1, position, centerItem - absCurrentScrollPosition + layoutCount - i); + } + mLayoutHelper.setLayoutOrder(layoutCount - 1, centerItem, centerItem - absCurrentScrollPosition); + + } else { + final int firstVisible = Math.max(centerItem - mLayoutHelper.mMaxVisibleItems, 0); + final int lastVisible = Math.min(centerItem + mLayoutHelper.mMaxVisibleItems, mItemsCount - 1); + final int layoutCount = lastVisible - firstVisible + 1; + + mLayoutHelper.initLayoutOrder(layoutCount); + + for (int i = firstVisible; i <= lastVisible; ++i) { + if (i == centerItem) { + mLayoutHelper.setLayoutOrder(layoutCount - 1, i, i - absCurrentScrollPosition); + } else if (i < centerItem) { + mLayoutHelper.setLayoutOrder(i - firstVisible, i, i - absCurrentScrollPosition); + } else { + mLayoutHelper.setLayoutOrder(layoutCount - (i - centerItem) - 1, i, i - absCurrentScrollPosition); + } + } + } + } + + public int getWidthNoPadding() { + return getWidth() - getPaddingStart() - getPaddingEnd(); + } + + public int getHeightNoPadding() { + return getHeight() - getPaddingEnd() - getPaddingStart(); + } + + private View bindChild(final int position, @NonNull final RecyclerView.Recycler recycler) { + final View view = recycler.getViewForPosition(position); + + addView(view); + measureChildWithMargins(view, 0, 0); + + return view; + } + + private void recyclerOldViews(final RecyclerView.Recycler recycler) { + for (RecyclerView.ViewHolder viewHolder : new ArrayList<>(recycler.getScrapList())) { + int adapterPosition = viewHolder.getAdapterPosition(); + boolean found = false; + for (LayoutOrder layoutOrder : mLayoutHelper.mLayoutOrder) { + if (layoutOrder.mItemAdapterPosition == adapterPosition) { + found = true; + break; + } + } + if (!found) { + recycler.recycleView(viewHolder.itemView); + } + } + } + + /** + * Called during {@link #fillData(RecyclerView.Recycler, RecyclerView.State)} to calculate item offset from layout center line.
+ *
+ * Returns {@link #convertItemPositionDiffToSmoothPositionDiff(float)} * (size off area above center item when it is on the center).
+ * Sign is: plus if this item is bellow center line, minus if not
+ *
+ * ----- - area above it
+ * ||||| - center item
+ * ----- - area bellow it (it has the same size as are above center item)
+ * + * @param itemPositionDiff current item difference with layout center line. if this is 0, then this item center is in layout center line. + * if this is 1 then this item is bellow the layout center line in the full item size distance. + * @return offset in scroll px coordinates. + */ + protected int getCardOffsetByPositionDiff(final float itemPositionDiff) { + final double smoothPosition = convertItemPositionDiffToSmoothPositionDiff(itemPositionDiff); + + final int dimenDiff; + if (VERTICAL == mOrientation) { + dimenDiff = (getHeightNoPadding() - mDecoratedChildHeight) / 2; + } else { + dimenDiff = (getWidthNoPadding() - mDecoratedChildWidth) / 2; + } + //noinspection NumericCastThatLosesPrecision + return (int) Math.round(Math.signum(itemPositionDiff) * dimenDiff * smoothPosition); + } + + /** + * Called during {@link #getCardOffsetByPositionDiff(float)} for better item movement.
+ * Current implementation speed up items that are far from layout center line and slow down items that are close to this line. + * This code is full of maths. If you want to make items move in a different way, probably you should override this method.
+ * Please see code comments for better explanations. + * + * @param itemPositionDiff current item difference with layout center line. if this is 0, then this item center is in layout center line. + * if this is 1 then this item is bellow the layout center line in the full item size distance. + * @return smooth position offset. needed for scroll calculation and better user experience. + * @see #getCardOffsetByPositionDiff(float) + */ + @SuppressWarnings({"MagicNumber", "InstanceMethodNamingConvention"}) + protected double convertItemPositionDiffToSmoothPositionDiff(final float itemPositionDiff) { + // generally item moves the same way above center and bellow it. So we don't care about diff sign. + final float absIemPositionDiff = Math.abs(itemPositionDiff); + + // we detect if this item is close for center or not. We use (1 / maxVisibleItem) ^ (1/3) as close definer. + if (absIemPositionDiff > StrictMath.pow(1.0f / mLayoutHelper.mMaxVisibleItems, 1.0f / 3)) { + // this item is far from center line, so we should make it move like square root function + return StrictMath.pow(absIemPositionDiff / mLayoutHelper.mMaxVisibleItems, 1 / 2.0f); + } else { + // this item is close from center line. we should slow it down and don't make it speed up very quick. + // so square function in range of [0, (1/maxVisible)^(1/3)] is quite good in it; + return StrictMath.pow(absIemPositionDiff, 2.0f); + } + } + + /** + * @return full item size + */ + protected int getScrollItemSize() { + if (VERTICAL == mOrientation) { + return mDecoratedChildHeight; + } else { + return mDecoratedChildWidth; + } + } + + @Override + public Parcelable onSaveInstanceState() { + if (null != mPendingCarouselSavedState) { + return new CarouselSavedState(mPendingCarouselSavedState); + } + final CarouselSavedState savedState = new CarouselSavedState(super.onSaveInstanceState()); + savedState.mCenterItemPosition = mCenterItemPosition; + return savedState; + } + + @Override + public void onRestoreInstanceState(final Parcelable state) { + if (state instanceof CarouselSavedState) { + mPendingCarouselSavedState = (CarouselSavedState) state; + + super.onRestoreInstanceState(mPendingCarouselSavedState.mSuperState); + } else { + super.onRestoreInstanceState(state); + } + } + + /** + * @return 从中心到最近项目的滚动偏移量 + */ + protected int getOffsetCenterView() { + return Math.round(getCurrentScrollPosition()) * getScrollItemSize() - mLayoutHelper.mScrollOffset; + } + + protected int getOffsetForCurrentView(@NonNull final View view) { + final int targetPosition = getPosition(view); + final float directionDistance = getScrollDirection(targetPosition); + + return Math.round(directionDistance * getScrollItemSize()); + } + + /** + * 使滚动范围在[0,count]内的Helper方法。通常,只有循环布局才需要此方法。 + * + * @param currentScrollPosition 滚动位置范围 个位数 view的index 小数滚动的范围 + * @param count adapter 中的数量 + * @return 在[0,总数]范围内滚动位置良好 + */ + private static float makeScrollPositionInRange0ToCount(final float currentScrollPosition, final int count) { + float absCurrentScrollPosition = currentScrollPosition; + while (0 > absCurrentScrollPosition) { + absCurrentScrollPosition += count; + } + while (Math.round(absCurrentScrollPosition) >= count) { + absCurrentScrollPosition -= count; + } + return absCurrentScrollPosition; + } + + /** + * This interface methods will be called for each visible view item after general LayoutManager layout finishes.
+ *
+ * Generally this method should be used for scaling and translating view item for better (different) view presentation of layouting. + */ + @SuppressWarnings("InterfaceNeverImplemented") + public abstract static class PostLayoutListener { + + /** + * 子布局完成后调用。通常,您可以在这里进行任何平移和缩放工作。 + * + * @param child view that was layout + * @param itemPositionToCenterDiff view center line difference to layout center. if > 0 then this item is bellow layout center line, else if not + * @param orientation layoutManager orientation {@link #getLayoutDirection()} + * @param itemPositionInAdapter item position inside adapter for this layout pass + */ + public ItemTransformation transformChild( + @NonNull final View child, + final float itemPositionToCenterDiff, + final int orientation, + final int itemPositionInAdapter + ) { + return transformChild(child, itemPositionToCenterDiff, orientation); + } + + /** + * Called after child layout finished. Generally you can do any translation and scaling work here. + * + * @param child view that was layout + * @param itemPositionToCenterDiff view center line difference to layout center. if > 0 then this item is bellow layout center line, else if not + * @param orientation layoutManager orientation {@link #getLayoutDirection()} + */ + public ItemTransformation transformChild( + @NonNull final View child, + final float itemPositionToCenterDiff, + final int orientation + ) { + throw new IllegalStateException("at least one transformChild should be implemented"); + } + } + + public interface OnCenterItemSelectionListener { + + /** + * Listener that will be called on every change of center item. + * This listener will be triggered on every layout operation if item was changed. + * Do not do any expensive operations in this method since this will effect scroll experience. + * + * @param adapterPosition current layout center item + */ + void onCenterItemChanged(final int adapterPosition); + } + + public interface OnDargAutoDiffListener { + void onDxChanged(final float adapterPosition,final int currentPosition); + } + + /** + * Helper class that holds currently visible items. + * Generally this class fills this list.
+ *
+ * This class holds all scroll and maxVisible items state. + * + * @see #getMaxVisibleItems() + */ + private static class LayoutHelper { + + private int mMaxVisibleItems; + + private int mScrollOffset; + + private LayoutOrder[] mLayoutOrder; + + private final List> mReusedItems = new ArrayList<>(); + + LayoutHelper(final int maxVisibleItems) { + mMaxVisibleItems = maxVisibleItems; + } + + /** + * Called before any fill calls. Needed to recycle old items and init new array list. Generally this list is an array an it is reused. + * + * @param layoutCount items count that will be layout + */ + void initLayoutOrder(final int layoutCount) { + if (null == mLayoutOrder || mLayoutOrder.length != layoutCount) { + if (null != mLayoutOrder) { + recycleItems(mLayoutOrder); + } + mLayoutOrder = new LayoutOrder[layoutCount]; + fillLayoutOrder(); + } + } + + /** + * Called during layout generation process of filling this list. Should be called only after {@link #initLayoutOrder(int)} method call. + * + * @param arrayPosition position in layout order + * @param itemAdapterPosition adapter position of item for future data filling logic + * @param itemPositionDiff difference of current item scroll position and center item position. + * if this is a center item and it is in real center of layout, then this will be 0. + * if current layout is not in the center, then this value will never be int. + * if this item center is bellow layout center line then this value is greater then 0, + * else less then 0. + */ + void setLayoutOrder(final int arrayPosition, final int itemAdapterPosition, final float itemPositionDiff) { + final LayoutOrder item = mLayoutOrder[arrayPosition]; + item.mItemAdapterPosition = itemAdapterPosition; + item.mItemPositionDiff = itemPositionDiff; + } + + /** + * Checks is this screen Layout has this adapterPosition view in layout + * + * @param adapterPosition adapter position of item for future data filling logic + * @return true is adapterItem is in layout + */ + boolean hasAdapterPosition(final int adapterPosition) { + if (null != mLayoutOrder) { + for (final LayoutOrder layoutOrder : mLayoutOrder) { + if (layoutOrder.mItemAdapterPosition == adapterPosition) { + return true; + } + } + } + return false; + } + + @SuppressWarnings("VariableArgumentMethod") + private void recycleItems(@NonNull final LayoutOrder... layoutOrders) { + for (final LayoutOrder layoutOrder : layoutOrders) { + //noinspection ObjectAllocationInLoop + mReusedItems.add(new WeakReference<>(layoutOrder)); + } + } + + private void fillLayoutOrder() { + for (int i = 0, length = mLayoutOrder.length; i < length; ++i) { + if (null == mLayoutOrder[i]) { + mLayoutOrder[i] = createLayoutOrder(); + } + } + } + + private LayoutOrder createLayoutOrder() { + final Iterator> iterator = mReusedItems.iterator(); + while (iterator.hasNext()) { + final WeakReference layoutOrderWeakReference = iterator.next(); + final LayoutOrder layoutOrder = layoutOrderWeakReference.get(); + iterator.remove(); + if (null != layoutOrder) { + return layoutOrder; + } + } + return new LayoutOrder(); + } + } + + /** + * Class that holds item data. + * This class is filled during {@link #generateLayoutOrder(float, RecyclerView.State)} and used during {@link #fillData(RecyclerView.Recycler, RecyclerView.State)} + */ + private static class LayoutOrder { + + /** + * Item adapter position + */ + private int mItemAdapterPosition; + /** + * Item center difference to layout center. If center of item is bellow layout center, then this value is greater then 0, else it is less. + */ + private float mItemPositionDiff; + } + + protected static class CarouselSavedState implements Parcelable { + + private final Parcelable mSuperState; + private int mCenterItemPosition; + + protected CarouselSavedState(@Nullable final Parcelable superState) { + mSuperState = superState; + } + + private CarouselSavedState(@NonNull final Parcel in) { + mSuperState = in.readParcelable(Parcelable.class.getClassLoader()); + mCenterItemPosition = in.readInt(); + } + + protected CarouselSavedState(@NonNull final CarouselSavedState other) { + mSuperState = other.mSuperState; + mCenterItemPosition = other.mCenterItemPosition; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(final Parcel parcel, final int i) { + parcel.writeParcelable(mSuperState, i); + parcel.writeInt(mCenterItemPosition); + } + + public static final Creator CREATOR + = new Creator() { + @Override + public CarouselSavedState createFromParcel(final Parcel parcel) { + return new CarouselSavedState(parcel); + } + + @Override + public CarouselSavedState[] newArray(final int i) { + return new CarouselSavedState[i]; + } + }; + } +} \ No newline at end of file diff --git a/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/layoutmanage/CarouselZoomPostLayoutListener.java b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/layoutmanage/CarouselZoomPostLayoutListener.java new file mode 100644 index 0000000000..7d56c32af3 --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/layoutmanage/CarouselZoomPostLayoutListener.java @@ -0,0 +1,40 @@ +package com.mogo.och.bus.passenger.ui.layoutmanage; + +import android.view.View; + +import androidx.annotation.NonNull; + +/** + * Implementation of {@link CarouselLayoutManager.PostLayoutListener} that makes interesting scaling of items.
+ * We are trying to make items scaling quicker for closer items for center and slower for when they are far away.
+ * Tis implementation uses atan function for this purpose. + */ +public class CarouselZoomPostLayoutListener extends CarouselLayoutManager.PostLayoutListener { + + private final float mScaleMultiplier; + + public CarouselZoomPostLayoutListener() { + this(0.21f); + } + + public CarouselZoomPostLayoutListener(final float scaleMultiplier) { + mScaleMultiplier = scaleMultiplier; + } + + @Override + public ItemTransformation transformChild(@NonNull final View child, final float itemPositionToCenterDiff, final int orientation) { + float scale = 1.0f - mScaleMultiplier * Math.abs(itemPositionToCenterDiff); + final float translateY; + final float translateX; + if (CarouselLayoutManager.VERTICAL == orientation) { + final float translateYGeneral = child.getMeasuredHeight() * (1 - scale) / 2f; + translateY = Math.signum(itemPositionToCenterDiff) * translateYGeneral; + translateX = 0; + } else { + final float translateXGeneral = child.getMeasuredWidth() * (1 - scale)/8; + translateX = Math.signum(itemPositionToCenterDiff) * translateXGeneral; + translateY = 0; + } + return new ItemTransformation(scale,scale, scale, 0, translateY); + } +} \ No newline at end of file diff --git a/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/layoutmanage/CenterScrollListener.java b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/layoutmanage/CenterScrollListener.java new file mode 100644 index 0000000000..2e69dd400e --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/layoutmanage/CenterScrollListener.java @@ -0,0 +1,52 @@ +package com.mogo.och.bus.passenger.ui.layoutmanage; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +/** + * Class for centering items after scroll event.
+ * This class will listen to current scroll state and if item is not centered after scroll it will automatically scroll it to center. + */ +public class CenterScrollListener extends RecyclerView.OnScrollListener { + + private boolean mAutoSet = true; + + @Override + public void onScrollStateChanged(@NonNull final RecyclerView recyclerView, final int newState) { + super.onScrollStateChanged(recyclerView, newState); + final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); + if (!(layoutManager instanceof CarouselLayoutManager)) { + mAutoSet = true; + return; + } + + final CarouselLayoutManager lm = (CarouselLayoutManager) layoutManager; + if (!mAutoSet) { + if (RecyclerView.SCROLL_STATE_IDLE == newState) { + final int scrollNeeded = lm.getOffsetCenterView(); + // 滚动到中心位置 + if (CarouselLayoutManager.HORIZONTAL == lm.getOrientation()) { + recyclerView.smoothScrollBy(scrollNeeded, 0); + } else { + recyclerView.smoothScrollBy(0, scrollNeeded); + } + pageSelect(recyclerView,newState); + mAutoSet = true; + } + } + if (RecyclerView.SCROLL_STATE_DRAGGING == newState || RecyclerView.SCROLL_STATE_SETTLING == newState) { + mAutoSet = false; + } + if(RecyclerView.SCROLL_STATE_DRAGGING == newState){ + pageStop(); + } + } + + protected void pageStop() { + + } + + protected void pageSelect(RecyclerView recyclerView, final int newState) { + + } +} \ No newline at end of file diff --git a/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/layoutmanage/ItemTransformation.java b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/layoutmanage/ItemTransformation.java new file mode 100644 index 0000000000..7d09aa4b92 --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/ui/layoutmanage/ItemTransformation.java @@ -0,0 +1,18 @@ +package com.mogo.och.bus.passenger.ui.layoutmanage; + +public class ItemTransformation { + + final float mAlpha; + final float mScaleX; + final float mScaleY; + final float mTranslationX; + final float mTranslationY; + + public ItemTransformation(final float alpha,final float scaleX, final float scaleY, final float translationX, final float translationY) { + mScaleX = scaleX; + mScaleY = scaleY; + mTranslationX = translationX; + mTranslationY = translationY; + mAlpha = alpha; + } +} \ No newline at end of file diff --git a/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/utils/FullVideoUtils.kt b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/utils/FullVideoUtils.kt new file mode 100644 index 0000000000..9aa6058d9d --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/utils/FullVideoUtils.kt @@ -0,0 +1,96 @@ +package com.mogo.och.bus.passenger.utils + +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Context +import android.view.View +import android.view.WindowManager +import com.mogo.och.bus.passenger.view.ConsultVideoPlayer +import com.shuyu.gsyvideoplayer.GSYVideoManager +import java.lang.Exception + +/** + * 视频全屏播放 + * + * @author yangyakun + */ +@SuppressLint("StaticFieldLeak") +object FullVideoUtils { + private const val TAG = "OverlayViewUtils" + private var windowManager: WindowManager? = null + + @Volatile + private var isShowing = false + + /** + * 记录上一次的View + */ + private var lastOverlayView: View? = null + + /** + * 添加覆盖View在Activity上面 + */ + @JvmOverloads + fun showOverlayView(context: Activity, overlayView: View, ani: Int = -1) { + if (windowManager == null) { + windowManager = context.windowManager + } + + // 设置View显示模式,沉浸式的侵入到状态栏,导航栏 + overlayView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + or View.SYSTEM_UI_FLAG_LAYOUT_STABLE + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) + val params = WindowManager.LayoutParams() + params.width = WindowManager.LayoutParams.MATCH_PARENT + params.height = WindowManager.LayoutParams.MATCH_PARENT + params.alpha = 1.0f + // 设置窗口类型为应用子窗口,和PopupWindow同类型 + params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY + // 没有边界限制,允许窗口扩展到屏幕外 + params.flags = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS + if (ani != -1) { + params.windowAnimations = ani + } + try { + // 后门逻辑,长时间触摸消失 + lastOverlayView = overlayView + windowManager!!.addView(overlayView, params) + isShowing = true + } catch (e: Exception) { + e.printStackTrace() + } + } + + /** + * 移除覆盖View在Activity上面 + */ + fun dismissOverlayView(needReleas:Boolean) { + if (!isShowing) { + return + } + val consultVideoPlayer = + lastOverlayView?.findViewById(GSYVideoManager.FULLSCREEN_ID) + consultVideoPlayer?.let { + if(needReleas){ + it.onVideoReset() + it.setVideoAllCallBack(null) + it.smalllPlayer?.clearFullscreenLayout(it) + } + consultVideoPlayer.removeAllViews() + } + try { + if (windowManager != null) { + windowManager!!.removeViewImmediate(lastOverlayView) + windowManager = null + } + if (lastOverlayView != null) { + lastOverlayView = null + } + isShowing = false + } catch (e: Exception) { + e.printStackTrace() + } + } +} \ No newline at end of file diff --git a/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/utils/ZoomDrawable.kt b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/utils/ZoomDrawable.kt new file mode 100644 index 0000000000..1811e3b1fd --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/utils/ZoomDrawable.kt @@ -0,0 +1,21 @@ +package com.mogo.och.bus.passenger.utils + +import android.content.Context +import android.graphics.* +import android.graphics.drawable.BitmapDrawable +import android.graphics.drawable.Drawable +import androidx.annotation.DrawableRes + +object ZoomDrawable { + fun zoomDrawableImage(context: Context,@DrawableRes id:Int,scaleX:Float,scaleY:Float):Drawable{ + + val bitmap: Bitmap = BitmapFactory.decodeResource(context.resources, id) + val bitmapWidth = bitmap.width + val bitmapHeight = bitmap.height + val matrix = Matrix() + matrix.postScale(scaleX, scaleY) + // 产生缩放后的Bitmap对象 + val resizeBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmapWidth, bitmapHeight, matrix, true) + return BitmapDrawable(context.resources,resizeBitmap) + } +} \ No newline at end of file diff --git a/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/view/ConsultVideoPlayer.kt b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/view/ConsultVideoPlayer.kt new file mode 100644 index 0000000000..c62073bf50 --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/java/com/mogo/och/bus/passenger/view/ConsultVideoPlayer.kt @@ -0,0 +1,484 @@ +package com.mogo.och.bus.passenger.view + +import android.app.Activity +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.GradientDrawable +import android.os.Build +import android.util.AttributeSet +import android.util.TypedValue +import android.view.Gravity +import android.view.Surface +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.TextView +import androidx.appcompat.widget.AppCompatImageView +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.app.ActivityCompat +import com.mogo.eagle.core.utilcode.util.TimeTransformUtils +import com.mogo.eagle.core.utilcode.util.ToastUtils +import com.mogo.eagle.core.widget.media.video.TextureVideoViewOutlineProvider +import com.mogo.och.bus.passenger.R +import com.mogo.och.bus.passenger.utils.FullVideoUtils +import com.mogo.och.bus.passenger.utils.ZoomDrawable +import com.shuyu.gsyvideoplayer.listener.VideoAllCallBack +import com.shuyu.gsyvideoplayer.utils.GSYVideoType +import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer +import com.shuyu.gsyvideoplayer.video.base.GSYBaseVideoPlayer +import com.shuyu.gsyvideoplayer.video.base.GSYVideoPlayer +import com.shuyu.gsyvideoplayer.video.base.GSYVideoView +import me.jessyan.autosize.utils.AutoSizeUtils +import java.lang.reflect.Constructor + +/** + * @author lixiaopeng + * @since 2021/11/3 + * + * 视频播放器,ui定制 + */ +class ConsultVideoPlayer : StandardGSYVideoPlayer { + + private lateinit var start: ImageView + lateinit var coverImage: ImageView + private lateinit var currentTimeTextView: TextView + private lateinit var totalTimeTextView: TextView + private lateinit var aivStartPlay: AppCompatImageView + private lateinit var layoutBottom: ConstraintLayout + private lateinit var vPpenLeft: View + + private var fullVideoPlayer:ConsultVideoPlayer?=null + var smalllPlayer:ConsultVideoPlayer?=null + + private var currentTime = 0 + + constructor(context: Context?) : super(context) + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + constructor(context: Context?, fullFlag: Boolean?) : super(context, fullFlag) + + override fun init(context: Context) { + mEnlargeImageRes = R.drawable.taxi_p_change_full + super.init(context) + start = findViewById(R.id.start) + coverImage = findViewById(R.id.thumbImage) + currentTimeTextView = findViewById(R.id.current) + totalTimeTextView = findViewById(R.id.total) + aivStartPlay = findViewById(R.id.aiv_start_play) + layoutBottom = findViewById(R.id.layout_bottom) + vPpenLeft = findViewById(R.id.v_open_left) + fullscreenButton.setOnClickListener(this) + aivStartPlay.setOnClickListener(this) + if (mThumbImageViewLayout != null + && (mCurrentState == -1 || mCurrentState == CURRENT_STATE_NORMAL || mCurrentState == CURRENT_STATE_ERROR) + ) { + mThumbImageViewLayout.visibility = View.VISIBLE + } + GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_FULL) + aivStartPlay.scaleX = 0.8f + aivStartPlay.scaleY = 0.8f + + mProgressBar.thumb = ZoomDrawable.zoomDrawableImage(context,R.drawable.bg_taxi_p_video_index,0.66f,0.66f) + } + + private fun addDrageAnchor(){ + vPpenLeft.visibility = VISIBLE + layoutBottom.post { + val layoutParams = layoutBottom.layoutParams as ConstraintLayout.LayoutParams + layoutParams.height = AutoSizeUtils.dp2px(context,176f) + layoutBottom.layoutParams = layoutParams + } + + mTopContainer.post { +// val layoutParams = mTopContainer.layoutParams as ConstraintLayout.LayoutParams +// layoutParams.height = 320 +// mTopContainer.layoutParams = layoutParams + val background = layoutBottom.background as GradientDrawable + val x = arrayOf(12f, 12f,12f, 12f,12f, 12f,12f, 12f) + background.cornerRadii = x.toFloatArray() + layoutBottom.background = background + + fullscreenButton.setPadding(92,0,92,0) + titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, AutoSizeUtils.dp2px(context,40f).toFloat()) + val layoutParams1 = titleTextView.layoutParams as ConstraintLayout.LayoutParams + layoutParams1.marginStart = 80 + titleTextView.layoutParams = layoutParams1 + aivStartPlay.scaleX = 1f + aivStartPlay.scaleY = 1f + val drawable = ActivityCompat.getDrawable(context, R.drawable.bg_taxi_p_video_index) + mProgressBar.thumb = drawable + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + mProgressBar.maxHeight = 6 + mProgressBar.minHeight = 6 + } + } + + } + + override fun getLayoutId(): Int { + return R.layout.taxi_p_video_show + } + + override fun updateStartImage() { + when (mCurrentState) { + GSYVideoView.CURRENT_STATE_PLAYING ->{ + start.setImageResource(R.drawable.notice_video_pause) + aivStartPlay.visibility = View.GONE + } + else -> { + start.setImageResource(R.drawable.notice_video_after_pause) + aivStartPlay.visibility = View.VISIBLE + } + } + } + + override fun setStateAndUi(state: Int) { + super.setStateAndUi(state) + if(state==CURRENT_STATE_PLAYING_BUFFERING_START){ + ToastUtils.showShort("加载中请稍等") + } + } + + override fun onWindowFocusChanged(hasWindowFocus: Boolean) { + super.onWindowFocusChanged(hasWindowFocus) + if(isIfCurrentIsFullscreen&&smalllPlayer!=null){ + if(hasWindowFocus){//获取焦点 + onVideoResume() + }else{ + onVideoPause() + } + } + } + + override fun touchDoubleUp() { + + } + + override fun changeUiToNormal() { + super.changeUiToNormal() + setViewShowState(fullscreenButton, INVISIBLE) + } + + override fun changeUiToPlayingShow() { + super.changeUiToPlayingShow() + setViewShowState(fullscreenButton, VISIBLE) + } + + public override fun hideAllWidget() { + super.hideAllWidget() + } + + override fun setProgressAndTime( + progress: Int, + secProgress: Int, + currentTime: Int, + totalTime: Int, + forceChange: Boolean + ) { + super.setProgressAndTime(progress, secProgress, currentTime, totalTime, forceChange) + //时间显示 + currentTimeTextView.text = TimeTransformUtils.stringForTime(currentTime) + totalTimeTextView.text = TimeTransformUtils.stringForTime(totalTime) + if(currentTime>=totalTime-3000){// + this.currentTime = -1 + }else{ + this.currentTime = currentTime + } + if (progress != 0) { + mProgressBar?.progress = progress + } + } + + override fun showWifiDialog() { + //直接播放,不显示WIFI对话框 + startPlayLogic() + } + + override fun onDetachedFromWindow() { + mProgressBar?.progress = 0 + fullVideoPlayer?.let { + clearFullscreenLayout(it) + } + fullVideoPlayer = null + if(!isIfCurrentIsFullscreen) { + onVideoReset() + setVideoAllCallBack(null) + } + dismissProgressDialog() + super.onDetachedFromWindow() + } + + override fun onClick(v: View?) { + super.onClick(v) + when (v?.id) { + R.id.fullscreen -> { + startWindowFullscreenOwn(context) +// startWindowFullscreen(context) + } + R.id.aiv_start_play -> { + if(currentState==GSYVideoView.CURRENT_STATE_PAUSE){ + onVideoResume(false) + }else{ + if (mProgressBar==null) { + startPlayLogic() + }else { + mProgressBar?.let { + if(currentTime>0) { + seekOnStart = currentTime.toLong() + } + startPlayLogic() + } + } + + } + } + else -> {} + } + } + + override fun onCompletion() { + start.setImageResource(R.drawable.notice_video_after_pause) + } + + override fun onSurfaceUpdated(surface: Surface) { + super.onSurfaceUpdated(surface) + if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == View.VISIBLE) { + mThumbImageViewLayout.visibility = View.INVISIBLE + } + } + + override fun onPrepared() { + super.onPrepared() + } + + override fun onBufferingUpdate(percent: Int) { + super.onBufferingUpdate(percent) + + } + + override fun onError(what: Int, extra: Int) { + super.onError(what, extra) + mThumbImageViewLayout?.visibility = View.VISIBLE + ToastUtils.showLong("哎呀,出错了,看看其他视频吧") + currentTime = -1 + if(isIfCurrentIsFullscreen){ + smalllPlayer?.clearFullscreenLayout(this) + smalllPlayer?.currentTime = -1 + FullVideoUtils.dismissOverlayView(false) + } + } + + override fun setViewShowState(view: View?, visibility: Int) { + if (view === mThumbImageViewLayout && visibility != View.VISIBLE) { + return + } + super.setViewShowState(view, visibility) + } + + override fun onSurfaceAvailable(surface: Surface) { + super.onSurfaceAvailable(surface) + if (GSYVideoType.getRenderType() != GSYVideoType.TEXTURE) { + if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == View.VISIBLE) { + mThumbImageViewLayout.visibility = View.INVISIBLE + } + } + } + + override fun onAutoCompletion() { + super.onAutoCompletion() + if(mIfCurrentIsFullscreen){ + if(smalllPlayer!=null){ + smalllPlayer?.clearFullscreenLayout(this) + } + FullVideoUtils.dismissOverlayView(false) + } + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + if (!mIfCurrentIsFullscreen) { + this.outlineProvider = TextureVideoViewOutlineProvider(38F) + this.clipToOutline = true + } + } + + private fun startWindowFullscreenOwn(context:Context){ + val gsyBaseVideoPlayer = startWindowFullscreen(context) + gsyBaseVideoPlayer?.let { + val gsyVideoPlayer = it as StandardGSYVideoPlayer + gsyVideoPlayer.setLockClickListener(mLockClickListener) + gsyVideoPlayer.isNeedLockFull = isNeedLockFull + initFullUI(gsyVideoPlayer) + } + } + + private fun initFullUI(standardGSYVideoPlayer: StandardGSYVideoPlayer) { + if (mBottomProgressDrawable != null) { + standardGSYVideoPlayer.setBottomProgressBarDrawable(mBottomProgressDrawable) + } + if (mBottomShowProgressDrawable != null && mBottomShowProgressThumbDrawable != null) { + standardGSYVideoPlayer.setBottomShowProgressBarDrawable( + mBottomShowProgressDrawable, + mBottomShowProgressThumbDrawable + ) + } + if (mVolumeProgressDrawable != null) { + standardGSYVideoPlayer.setDialogVolumeProgressBar(mVolumeProgressDrawable) + } + if (mDialogProgressBarDrawable != null) { + standardGSYVideoPlayer.setDialogProgressBar(mDialogProgressBarDrawable) + } + if (mDialogProgressHighLightColor >= 0 && mDialogProgressNormalColor >= 0) { + standardGSYVideoPlayer.setDialogProgressColor( + mDialogProgressHighLightColor, + mDialogProgressNormalColor + ) + } + standardGSYVideoPlayer.titleTextView?.text = titleTextView.text + } + + private fun startWindowFullscreen(context:Context):GSYBaseVideoPlayer?{ + if (mTextureViewContainer.childCount > 0) { + mTextureViewContainer.removeAllViews() + } + var hadNewConstructor = true + + //切换时关闭非全屏定时器 + cancelProgressTimer() + try { + this@ConsultVideoPlayer.javaClass.getConstructor( + Context::class.java, + Boolean::class.java + ) + } catch (e: java.lang.Exception) { + hadNewConstructor = false + } + try { + //通过被重载的不同构造器来选择 + val constructor: Constructor + val gsyVideoPlayer: ConsultVideoPlayer + if (!hadNewConstructor) { + constructor = this@ConsultVideoPlayer.javaClass.getConstructor(Context::class.java) + gsyVideoPlayer = constructor.newInstance(mContext) + } else { + constructor = this@ConsultVideoPlayer.javaClass.getConstructor( + Context::class.java, + Boolean::class.java + ) + gsyVideoPlayer = constructor.newInstance(mContext, true) + } + this.fullVideoPlayer = gsyVideoPlayer + gsyVideoPlayer.id = fullId + gsyVideoPlayer.isIfCurrentIsFullscreen = true + gsyVideoPlayer.setVideoAllCallBack(mVideoAllCallBack) + gsyVideoPlayer.addDrageAnchor() + cloneParams(this, gsyVideoPlayer) + val frameLayout = FrameLayout(context) + if (gsyVideoPlayer.fullscreenButton != null) { + gsyVideoPlayer.fullscreenButton.setImageResource(R.drawable.taxi_p_change_normal) + gsyVideoPlayer.start.setImageResource(R.drawable.notice_video_pause_big) + gsyVideoPlayer.fullscreenButton.setOnClickListener { v -> + if (mBackFromFullScreenListener == null) { + clearFullscreenLayout(gsyVideoPlayer) + FullVideoUtils.dismissOverlayView(false) + } else { + mBackFromFullScreenListener.onClick(v) + } + } + } + gsyVideoPlayer.smalllPlayer = this + frameLayout.setBackgroundColor(Color.BLACK) + val lp = LayoutParams(width, height) + frameLayout.addView(gsyVideoPlayer, lp) + FullVideoUtils.showOverlayView(context as Activity,frameLayout,R.style.och_window_anim_alpha) + gsyVideoPlayer.visibility = INVISIBLE + frameLayout.visibility = INVISIBLE + resolveFullVideoShow(context, gsyVideoPlayer, frameLayout) + gsyVideoPlayer.addTextureView() + gsyVideoPlayer.startProgressTimer() + gsyVideoManager.setLastListener(this) + gsyVideoManager.setListener(gsyVideoPlayer) + checkoutState() + thumbImageViewLayout.visibility = View.VISIBLE + return gsyVideoPlayer + } catch (e: java.lang.Exception) { + e.printStackTrace() + } + + return null + } + + /** + * 全屏 + */ + override fun resolveFullVideoShow(context: Context?, gsyVideoPlayer: GSYBaseVideoPlayer, + frameLayout: FrameLayout) { + val lp = gsyVideoPlayer.layoutParams as LayoutParams + lp.setMargins(0, 0, 0, 0) + lp.height = ViewGroup.LayoutParams.WRAP_CONTENT + lp.width = ViewGroup.LayoutParams.MATCH_PARENT + lp.gravity = Gravity.BOTTOM + gsyVideoPlayer.layoutParams = lp + gsyVideoPlayer.isIfCurrentIsFullscreen = true + val isVertical = isVerticalFullByVideoSize + val isLockLand = isLockLandByAutoFullSize + if (isShowFullAnimation) { + mInnerHandler.postDelayed({ //autoFull模式下,非横屏视频视频不横屏,并且不自动旋转 + if (!isVertical && isLockLand && mOrientationUtils != null && mOrientationUtils.isLand != 1) { + mOrientationUtils.resolveByClick() + } + gsyVideoPlayer.visibility = VISIBLE + frameLayout.visibility = VISIBLE + }, 300) + } else { + if (!isVertical && isLockLand && mOrientationUtils != null) { + mOrientationUtils.resolveByClick() + } + gsyVideoPlayer.visibility = VISIBLE + frameLayout.visibility = VISIBLE + } + if (mVideoAllCallBack != null) { + mVideoAllCallBack.onEnterFullscreen(mOriginUrl, mTitle, gsyVideoPlayer) + } + mIfCurrentIsFullscreen = true + checkoutState() + checkAutoFullWithSizeAndAdaptation(gsyVideoPlayer) + } + + + + fun getStatusBarHeight(): Int{ + return Math.ceil((25 * context.resources.displayMetrics.density).toDouble()).toInt() + } + + fun clearFullscreenLayout(gsyVideoPlayer:ConsultVideoPlayer) { + mIfCurrentIsFullscreen = false + val delay = 100 + gsyVideoPlayer.smalllPlayer = null + mInnerHandler.postDelayed({ resolveNormalVideoShow(gsyVideoPlayer) }, delay.toLong()) + } + + private fun resolveNormalVideoShow(gsyVideoPlayer: GSYVideoPlayer) { + mCurrentState = gsyVideoManager.lastState + cloneParams(gsyVideoPlayer, this) + gsyVideoManager.setListener(gsyVideoManager.lastListener()) + gsyVideoManager.setLastListener(null) + gsyVideoPlayer.setVideoAllCallBack(null) + setStateAndUi(mCurrentState) + addTextureView() + mSaveChangeViewTIme = System.currentTimeMillis() + if (mVideoAllCallBack != null) { + mVideoAllCallBack.onQuitFullscreen(mOriginUrl, mTitle, this) + } + mIfCurrentIsFullscreen = false + if (fullscreenButton != null) { + fullscreenButton.setImageResource(enlargeImageRes) + } + this.fullVideoPlayer = null + } + + fun getVideoAllCallBack(): VideoAllCallBack? { + return mVideoAllCallBack + } +} + diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/anim/alpha_hide_show.xml b/OCH/mogo-och-bus-passenger/src/m1/res/anim/alpha_hide_show.xml new file mode 100644 index 0000000000..ddb2eaf470 --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/res/anim/alpha_hide_show.xml @@ -0,0 +1,3 @@ + + \ No newline at end of file diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/bg_taxi_p_video_index.png b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/bg_taxi_p_video_index.png new file mode 100644 index 0000000000..61f22d3bf0 Binary files /dev/null and b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/bg_taxi_p_video_index.png differ diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/notice_video_pause_big.png b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/notice_video_pause_big.png new file mode 100644 index 0000000000..5e58000eec Binary files /dev/null and b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/notice_video_pause_big.png differ diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/notice_video_pause_small.png b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/notice_video_pause_small.png new file mode 100644 index 0000000000..1fec2169b9 Binary files /dev/null and b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/notice_video_pause_small.png differ diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_change_full.png b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_change_full.png new file mode 100644 index 0000000000..a4524b5322 Binary files /dev/null and b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_change_full.png differ diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_change_normal.png b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_change_normal.png new file mode 100644 index 0000000000..b3ceb81fb6 Binary files /dev/null and b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_change_normal.png differ diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_mogo_video_play.png b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_mogo_video_play.png new file mode 100644 index 0000000000..ff5fb41cab Binary files /dev/null and b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_mogo_video_play.png differ diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_video_bg_bottom.png b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_video_bg_bottom.png new file mode 100644 index 0000000000..4192d9cbd7 Binary files /dev/null and b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_video_bg_bottom.png differ diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_video_bg_top.png b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_video_bg_top.png new file mode 100644 index 0000000000..f9b70e6888 Binary files /dev/null and b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_video_bg_top.png differ diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_video_holder.png b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_video_holder.png new file mode 100644 index 0000000000..99bfcefc6f Binary files /dev/null and b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-nodpi/taxi_p_video_holder.png differ diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/drawable-v29/taxi_video_seekbar_style.xml b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-v29/taxi_video_seekbar_style.xml new file mode 100644 index 0000000000..4a771923d6 --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/res/drawable-v29/taxi_video_seekbar_style.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/drawable/bg_taxi_p_video_bg.xml b/OCH/mogo-och-bus-passenger/src/m1/res/drawable/bg_taxi_p_video_bg.xml new file mode 100644 index 0000000000..b789f6c2ea --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/res/drawable/bg_taxi_p_video_bg.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/drawable/bg_taxi_p_video_bg_shape.xml b/OCH/mogo-och-bus-passenger/src/m1/res/drawable/bg_taxi_p_video_bg_shape.xml new file mode 100644 index 0000000000..f4cf3c03ad --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/res/drawable/bg_taxi_p_video_bg_shape.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/drawable/bg_taxi_p_video_bg_top.xml b/OCH/mogo-och-bus-passenger/src/m1/res/drawable/bg_taxi_p_video_bg_top.xml new file mode 100644 index 0000000000..843c2658d2 --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/res/drawable/bg_taxi_p_video_bg_top.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/drawable/taxi_video_seekbar_style.xml b/OCH/mogo-och-bus-passenger/src/m1/res/drawable/taxi_video_seekbar_style.xml new file mode 100644 index 0000000000..c7f6f83c1c --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/res/drawable/taxi_video_seekbar_style.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/layout/bus_p_function_video_fragment.xml b/OCH/mogo-och-bus-passenger/src/m1/res/layout/bus_p_function_video_fragment.xml index 301276e74a..3dd1e32646 100644 --- a/OCH/mogo-och-bus-passenger/src/m1/res/layout/bus_p_function_video_fragment.xml +++ b/OCH/mogo-och-bus-passenger/src/m1/res/layout/bus_p_function_video_fragment.xml @@ -2,16 +2,16 @@ - + app:layout_constraintBottom_toBottomOf="parent" + /> \ No newline at end of file diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/layout/list_video_item_normal.xml b/OCH/mogo-och-bus-passenger/src/m1/res/layout/list_video_item_normal.xml new file mode 100644 index 0000000000..92db957ac2 --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/res/layout/list_video_item_normal.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/layout/taxi_p_video_show.xml b/OCH/mogo-och-bus-passenger/src/m1/res/layout/taxi_p_video_show.xml new file mode 100644 index 0000000000..811e807de0 --- /dev/null +++ b/OCH/mogo-och-bus-passenger/src/m1/res/layout/taxi_p_video_show.xml @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OCH/mogo-och-bus-passenger/src/m1/res/values/styles.xml b/OCH/mogo-och-bus-passenger/src/m1/res/values/styles.xml index 9862547eb8..0951fd761f 100644 --- a/OCH/mogo-och-bus-passenger/src/m1/res/values/styles.xml +++ b/OCH/mogo-och-bus-passenger/src/m1/res/values/styles.xml @@ -20,6 +20,10 @@ false + + \ No newline at end of file diff --git a/OCH/mogo-och-taxi-passenger/build.gradle b/OCH/mogo-och-taxi-passenger/build.gradle index 6d9432cc53..49bb7f3f60 100644 --- a/OCH/mogo-och-taxi-passenger/build.gradle +++ b/OCH/mogo-och-taxi-passenger/build.gradle @@ -56,6 +56,7 @@ dependencies { implementation project(":OCH:mogo-och-common-module") compileOnly project(":libraries:mogo-map") + implementation project(':core:mogo-core-res') }