|
@@ -1,2286 +1,2286 @@
|
|
|
-package com.cooleshow.student.ui.live;
|
|
|
-
|
|
|
-import android.Manifest;
|
|
|
-import android.animation.ObjectAnimator;
|
|
|
-import android.content.ComponentName;
|
|
|
-import android.content.Context;
|
|
|
-import android.content.DialogInterface;
|
|
|
-import android.content.Intent;
|
|
|
-import android.content.ServiceConnection;
|
|
|
-import android.content.pm.ActivityInfo;
|
|
|
-import android.content.res.Configuration;
|
|
|
-import android.graphics.Color;
|
|
|
-import android.net.Uri;
|
|
|
-import android.os.Bundle;
|
|
|
-import android.os.Handler;
|
|
|
-import android.os.IBinder;
|
|
|
-import android.os.Looper;
|
|
|
-import android.provider.Settings;
|
|
|
-import android.text.TextUtils;
|
|
|
-import android.util.Log;
|
|
|
-import android.view.View;
|
|
|
-import android.view.WindowManager;
|
|
|
-import android.widget.FrameLayout;
|
|
|
-import android.widget.ImageView;
|
|
|
-import android.widget.LinearLayout;
|
|
|
-import android.widget.RelativeLayout;
|
|
|
-import android.widget.TextView;
|
|
|
-import android.widget.Toast;
|
|
|
-
|
|
|
-import com.airbnb.lottie.LottieAnimationView;
|
|
|
-import com.alibaba.android.arouter.facade.annotation.Route;
|
|
|
-import com.alibaba.android.arouter.launcher.ARouter;
|
|
|
-import com.bumptech.glide.Glide;
|
|
|
-import com.cooleshow.base.common.WebConstants;
|
|
|
-import com.cooleshow.base.constanst.EventConstants;
|
|
|
-import com.cooleshow.base.data.net.ApiException;
|
|
|
-import com.cooleshow.base.router.RouterPath;
|
|
|
-import com.cooleshow.base.ui.activity.BaseMVPActivity;
|
|
|
-import com.cooleshow.base.utils.AppUtils;
|
|
|
-import com.cooleshow.base.utils.PermissionUtils;
|
|
|
-import com.cooleshow.base.utils.SizeUtils;
|
|
|
-import com.cooleshow.base.utils.SoftKeyboardUtil;
|
|
|
-import com.cooleshow.base.utils.ToastUtil;
|
|
|
-import com.cooleshow.base.utils.UiUtils;
|
|
|
-import com.cooleshow.base.utils.Utils;
|
|
|
-import com.cooleshow.base.widgets.InputBar;
|
|
|
-import com.cooleshow.base.widgets.dialog.CommonConfirmDialog;
|
|
|
-import com.cooleshow.chatmodule.constants.TCChatRouterPath;
|
|
|
-import com.cooleshow.student.bean.FriendInfoBean;
|
|
|
-import com.cooleshow.student.helper.EventHelper;
|
|
|
-import com.cooleshow.student.helper.ShareHelper;
|
|
|
-import com.cooleshow.student.ui.live.floatPop.FloatWindowHelper;
|
|
|
-import com.cooleshow.student.ui.live.floatPop.FloatingWindowService;
|
|
|
-import com.cooleshow.student.widgets.dialog.LiveRoomCloseTipDialog;
|
|
|
-import com.cooleshow.student.widgets.dialog.LiveRoomMicManagerDialog;
|
|
|
-import com.cooleshow.student.widgets.dialog.LiveRoomShareDialog;
|
|
|
-import com.cooleshow.student.widgets.dialog.OpenOverlayPermissionTipDialog;
|
|
|
-import com.cooleshow.student.widgets.helper.LiveRoomAddLikeHelper;
|
|
|
-import com.cooleshow.base.widgets.dialog.InputBarDialog;
|
|
|
-import com.cooleshow.student.R;
|
|
|
-import com.cooleshow.student.adapter.MessageAdapter;
|
|
|
-import com.cooleshow.student.bean.LiveRoomInfoBean;
|
|
|
-import com.cooleshow.student.contract.LiveRoomContract;
|
|
|
-import com.cooleshow.student.databinding.ActivityLiveroomLayoutBinding;
|
|
|
-import com.cooleshow.student.presenter.live.LiveRoomPresenter;
|
|
|
-import com.rong.io.live.helper.LiveMemberHelper;
|
|
|
-import com.rong.io.live.helper.LiveMessageHelper;
|
|
|
-import com.rong.io.live.helper.LiveRTCEngineInitHelper;
|
|
|
-import com.cooleshow.student.widgets.dialog.LiveRoomCloseMicTipDialog;
|
|
|
-import com.cooleshow.student.widgets.dialog.LiveRoomClosePageOnMicTipDialog;
|
|
|
-import com.cooleshow.student.widgets.dialog.LiveRoomInviteSeatMicTipDialog;
|
|
|
-import com.cooleshow.student.widgets.dialog.LiveRoomShopCarDialog;
|
|
|
-import com.cooleshow.student.widgets.helper.LiveRoomAnimatorHelper;
|
|
|
-import com.cooleshow.usercenter.helper.UserHelper;
|
|
|
-import com.rong.io.live.LiveRoomMsgConstants;
|
|
|
-import com.rong.io.live.config.LiveConfig;
|
|
|
-import com.rong.io.live.helper.LiveEventHelper;
|
|
|
-import com.rong.io.live.message.RCChatJoinRoomMessage;
|
|
|
-import com.rong.io.live.message.RCOnSnappingUpMessage;
|
|
|
-import com.rong.io.live.message.RCUserKickOutMessage;
|
|
|
-import com.rong.io.live.message.RCUserLogOutUnusualMessage;
|
|
|
-import com.rong.io.live.message.RCUserSeatApplyMessage;
|
|
|
-import com.rong.io.live.message.RCUserSeatResponseMessage;
|
|
|
-import com.rong.io.live.message.RCUserSyncAddLikeCountMessage;
|
|
|
-import com.rong.io.live.widget.LiveRoomMicIconView;
|
|
|
-
|
|
|
-import org.json.JSONException;
|
|
|
-import org.json.JSONObject;
|
|
|
-
|
|
|
-import java.io.File;
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.List;
|
|
|
-
|
|
|
-import androidx.annotation.NonNull;
|
|
|
-import androidx.annotation.Nullable;
|
|
|
-import androidx.constraintlayout.widget.ConstraintLayout;
|
|
|
-import androidx.constraintlayout.widget.ConstraintSet;
|
|
|
-import androidx.constraintlayout.widget.Group;
|
|
|
-import androidx.recyclerview.widget.LinearLayoutManager;
|
|
|
-import androidx.recyclerview.widget.RecyclerView;
|
|
|
-import cn.rongcloud.rtc.api.RCRTCAudioRouteManager;
|
|
|
-import cn.rongcloud.rtc.api.RCRTCRemoteUser;
|
|
|
-import cn.rongcloud.rtc.api.RCRTCRoom;
|
|
|
-import cn.rongcloud.rtc.api.RCRTCVideoStream;
|
|
|
-import cn.rongcloud.rtc.api.stream.RCRTCAudioInputStream;
|
|
|
-import cn.rongcloud.rtc.api.stream.RCRTCAudioOutputStream;
|
|
|
-import cn.rongcloud.rtc.api.stream.RCRTCInputStream;
|
|
|
-import cn.rongcloud.rtc.api.stream.RCRTCVideoInputStream;
|
|
|
-import cn.rongcloud.rtc.api.stream.RCRTCVideoOutputStream;
|
|
|
-import cn.rongcloud.rtc.api.stream.RCRTCVideoView;
|
|
|
-import cn.rongcloud.rtc.base.RCRTCLiveRole;
|
|
|
-import cn.rongcloud.rtc.base.RCRTCMediaType;
|
|
|
-import cn.rongcloud.rtc.base.RCRTCResourceState;
|
|
|
-import de.hdodenhof.circleimageview.CircleImageView;
|
|
|
-import io.reactivex.rxjava3.disposables.Disposable;
|
|
|
-import io.rong.imkit.IMCenter;
|
|
|
-import io.rong.imlib.IRongCoreCallback;
|
|
|
-import io.rong.imlib.IRongCoreEnum;
|
|
|
-import io.rong.imlib.RongIMClient;
|
|
|
-import io.rong.imlib.model.Conversation;
|
|
|
-import io.rong.imlib.model.Message;
|
|
|
-import io.rong.imlib.model.MessageContent;
|
|
|
-import io.rong.imlib.model.UserInfo;
|
|
|
-import io.rong.message.TextMessage;
|
|
|
-
|
|
|
-import com.cooleshow.student.widgets.helper.VideoViewManager;
|
|
|
-import com.tbruyelle.rxpermissions3.RxPermissions;
|
|
|
-import com.umeng.socialize.ShareAction;
|
|
|
-import com.umeng.socialize.UMShareAPI;
|
|
|
-import com.umeng.socialize.UMShareListener;
|
|
|
-import com.umeng.socialize.bean.SHARE_MEDIA;
|
|
|
-import com.umeng.socialize.media.UMImage;
|
|
|
-import com.umeng.socialize.shareboard.SnsPlatform;
|
|
|
-import com.umeng.socialize.utils.ShareBoardlistener;
|
|
|
-
|
|
|
-
|
|
|
- * Author by pq, Date on 2022/3/29.
|
|
|
- */
|
|
|
-
|
|
|
-@Route(path = RouterPath.LiveCenter.ACTIVITY_LIVE_ROOM)
|
|
|
-public class LiveRoomActivity extends BaseMVPActivity<ActivityLiveroomLayoutBinding, LiveRoomPresenter> implements View.OnClickListener, LiveRoomContract.view, SoftKeyboardUtil.OnSoftInputChangedListener, UMShareListener {
|
|
|
- public static final int SEND_SHOW_FLOAT_WINDOW_TIME = 1000;
|
|
|
- public static final int SEND_APP_BACKGROUND_MSG = 1001;
|
|
|
- public static final int SEND_JUMP_OTHER_PAGE_MSG = 1002;
|
|
|
- public static final int OPEN_SHOP_CAR_DIALOG = 1003;
|
|
|
- public static final int LIVE_STATUS_IS_REST = 1;
|
|
|
- public static final int LIVE_STATUS_IS_CLOSE_VIDEO = 2;
|
|
|
- public static final int LIVE_STATUS_IS_OFFLINE = 3;
|
|
|
- public static final int SHARE_CHAT_REQUEST_CODE = 500;
|
|
|
- private File targetShareImgFile = null;
|
|
|
- ConstraintLayout content_view;
|
|
|
- RelativeLayout mFlLiveView;
|
|
|
- LinearLayout ll_like;
|
|
|
- RecyclerView mRecyclerMsg;
|
|
|
- TextView mTvNumPeople;
|
|
|
- ImageView iv_mic;
|
|
|
- LiveRoomMicIconView mLlMicContainer;
|
|
|
- TextView mTvAddLikeCount;
|
|
|
- CircleImageView mIvAvatar;
|
|
|
- TextView mTvRoomCreateName;
|
|
|
- Group mGroupViews;
|
|
|
- ConstraintLayout mViewLiveStatus;
|
|
|
- ImageView mIvLiveStatusCenterIcon;
|
|
|
- TextView mTvLiveStatusTipText;
|
|
|
- ImageView mIvSwitchVideoOrientation;
|
|
|
- ImageView mIvSwitchVideoOrientationFull;
|
|
|
- ConstraintLayout cs_header_info;
|
|
|
- LottieAnimationView mViewShopCarAnim;
|
|
|
- ImageView mIvShopCar;
|
|
|
- ImageView mIvClose;
|
|
|
- TextView tv_input;
|
|
|
- FrameLayout mFlJoinBarrage;
|
|
|
- TextView mTvJoinBarrage;
|
|
|
- FrameLayout mFlSnapUpBarrage;
|
|
|
- TextView mTvSnapUpBarrage;
|
|
|
-
|
|
|
- private LiveRoomInfoBean mRoomInfoBean;
|
|
|
- private String mRoomId;
|
|
|
- private MessageAdapter mMessageAdapter;
|
|
|
- private LinearLayoutManager mLinearLayoutManager;
|
|
|
- private boolean isEnableChat = false;
|
|
|
- private boolean isEnableMic = false;
|
|
|
- private boolean isEnableAll = false;
|
|
|
- private InputBarDialog mInputBarDialog;
|
|
|
- private boolean isPcClientLive = true;
|
|
|
- private int currentSeatStatus = LiveRoomMsgConstants.MIC_STATUS_NORMAL;
|
|
|
- private LiveRoomCloseMicTipDialog mRoomCloseMicTipDialog;
|
|
|
- private LiveRoomInviteSeatMicTipDialog mInviteSeatMicTipDialog;
|
|
|
- private int currentAddLikeCount = 0;
|
|
|
- private boolean creatorIsCloseAudioStream = false;
|
|
|
- private boolean creatorIsCloseVideoStream = false;
|
|
|
- private String createRoomId = "";
|
|
|
- private static final String ROOMID_KEY = "roomid_key";
|
|
|
- private String mUserId;
|
|
|
- private String mImUserId;
|
|
|
- private boolean connectStatusIsPaused = false;
|
|
|
- private boolean isNeedRefresh = false;
|
|
|
- private boolean liveIsFinish = false;
|
|
|
- private boolean liveIsPause = false;
|
|
|
- private ServiceConnection serviceConnection;
|
|
|
- private ObjectAnimator hideHeaderInfoAnim;
|
|
|
- private ObjectAnimator showHeaderInfoAnim;
|
|
|
- private boolean isOnResume = true;
|
|
|
- private boolean isAutoScrollToLast = true;
|
|
|
- private Handler mHandler = new Handler(Looper.myLooper()) {
|
|
|
- @Override
|
|
|
- public void handleMessage(@NonNull android.os.Message msg) {
|
|
|
- if (msg.what == SEND_APP_BACKGROUND_MSG) {
|
|
|
-
|
|
|
-
|
|
|
- if (!isOnResume && AppUtils.isApplicationInBackground(LiveRoomActivity.this)) {
|
|
|
- startFloatWindowService();
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
- if (msg.what == SEND_JUMP_OTHER_PAGE_MSG) {
|
|
|
-
|
|
|
-
|
|
|
- if (isOnResume) {
|
|
|
-
|
|
|
- return;
|
|
|
- }
|
|
|
- startFloatWindowService();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (msg.what == OPEN_SHOP_CAR_DIALOG) {
|
|
|
- showShopCarDialog();
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
- private RongIMClient.ConnectionStatusListener connectStatusListener = new RongIMClient.ConnectionStatusListener() {
|
|
|
- @Override
|
|
|
- public void onChanged(ConnectionStatus status) {
|
|
|
- Log.i("pq", "LiveRoomActivity receive ConnectionStatus:" + status);
|
|
|
- if (status == ConnectionStatus.KICKED_OFFLINE_BY_OTHER_CLIENT
|
|
|
- || status == ConnectionStatus.SIGN_OUT || status == ConnectionStatus.TIMEOUT) {
|
|
|
- finish();
|
|
|
- }
|
|
|
- if (status == ConnectionStatus.CONNECTED) {
|
|
|
-
|
|
|
- if (presenter != null && isNeedRefresh) {
|
|
|
- connectStatusIsPaused = false;
|
|
|
- presenter.init(mRoomId, false);
|
|
|
- }
|
|
|
- } else {
|
|
|
-
|
|
|
- isNeedRefresh = true;
|
|
|
- }
|
|
|
- if (status == ConnectionStatus.NETWORK_UNAVAILABLE) {
|
|
|
-
|
|
|
- ToastUtil.getInstance().showShort("您的网络已断开,请检查网络~");
|
|
|
- }
|
|
|
- if (status == ConnectionStatus.SUSPEND) {
|
|
|
- connectStatusIsPaused = true;
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
- private LiveRoomClosePageOnMicTipDialog mLiveRoomClosePageOnMicTipDialog;
|
|
|
- private VideoViewManager mVideoViewManager;
|
|
|
- private LiveRoomShopCarDialog mShopCarDialog;
|
|
|
- private LiveRoomMicManagerDialog mMicManagerDialog;
|
|
|
- private LiveRoomShareDialog mLiveRoomShareDialog;
|
|
|
-
|
|
|
- public static void startLiveRoomActivity(Context context, String roomid) {
|
|
|
- Intent intent = new Intent(context, LiveRoomActivity.class);
|
|
|
- intent.putExtra(ROOMID_KEY, roomid);
|
|
|
- context.startActivity(intent);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void onCreate(Bundle savedInstanceState) {
|
|
|
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
|
|
- super.onCreate(savedInstanceState);
|
|
|
-
|
|
|
- EventHelper.addEvent(EventConstants.EVENT_ID_LIVE);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void initView() {
|
|
|
- content_view = viewBinding.contentView;
|
|
|
- mFlLiveView = viewBinding.flLiveView;
|
|
|
- ll_like = viewBinding.llLike;
|
|
|
- mRecyclerMsg = viewBinding.recyclerMsg;
|
|
|
- mTvNumPeople = viewBinding.tvNumPeople;
|
|
|
- iv_mic = viewBinding.ivMic;
|
|
|
- mLlMicContainer = viewBinding.llMicContainer;
|
|
|
- mTvAddLikeCount = viewBinding.tvAddLikeCount;
|
|
|
- mIvAvatar = viewBinding.ivAvatar;
|
|
|
- mTvRoomCreateName = viewBinding.tvRoomAuthorName;
|
|
|
- mGroupViews = viewBinding.groupViews;
|
|
|
- mViewLiveStatus = viewBinding.viewLiveStatus.csRootLiveStatus;
|
|
|
- mIvLiveStatusCenterIcon = viewBinding.viewLiveStatus.ivCenterIcon;
|
|
|
- mTvLiveStatusTipText = viewBinding.viewLiveStatus.tvLiveStatusTipText;
|
|
|
- mIvSwitchVideoOrientation = viewBinding.ivSwitchVideoOrientation;
|
|
|
- mIvSwitchVideoOrientationFull = viewBinding.ivSwitchVideoOrientationFull;
|
|
|
- cs_header_info = viewBinding.csHeaderInfo;
|
|
|
- mViewShopCarAnim = viewBinding.viewShopCarAnim;
|
|
|
- mIvShopCar = viewBinding.ivShopCar;
|
|
|
- mIvClose = viewBinding.ivClose;
|
|
|
- tv_input = viewBinding.tvInput;
|
|
|
- mFlJoinBarrage = viewBinding.flJoinBarrage;
|
|
|
- mTvJoinBarrage = viewBinding.tvJoinBarrage;
|
|
|
- mFlSnapUpBarrage = viewBinding.flSnapUpBarrage;
|
|
|
- mTvSnapUpBarrage = viewBinding.tvSnapUpBarrage;
|
|
|
- initListener();
|
|
|
- }
|
|
|
-
|
|
|
- private void initListener() {
|
|
|
- mIvClose.setOnClickListener(this);
|
|
|
- mIvSwitchVideoOrientation.setOnClickListener(this);
|
|
|
- mIvSwitchVideoOrientationFull.setOnClickListener(this);
|
|
|
- tv_input.setOnClickListener(this);
|
|
|
- iv_mic.setOnClickListener(this);
|
|
|
- viewBinding.iconAddLike.setOnClickListener(this);
|
|
|
- mViewLiveStatus.setOnClickListener(this);
|
|
|
- mViewShopCarAnim.setOnClickListener(this);
|
|
|
- mFlLiveView.setOnClickListener(this);
|
|
|
- viewBinding.tvAppointmentCourse.setOnClickListener(this);
|
|
|
- viewBinding.tvLiveCourse.setOnClickListener(this);
|
|
|
- viewBinding.tvVideoCourse.setOnClickListener(this);
|
|
|
- viewBinding.tvMusicSheet.setOnClickListener(this);
|
|
|
- viewBinding.ivAvatar.setOnClickListener(this);
|
|
|
- viewBinding.ivShare.setOnClickListener(this);
|
|
|
-
|
|
|
- mRecyclerMsg.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
|
|
- @Override
|
|
|
- public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
|
|
|
- super.onScrollStateChanged(recyclerView, newState);
|
|
|
- if (mLinearLayoutManager == null || mMessageAdapter == null) {
|
|
|
- return;
|
|
|
- }
|
|
|
- int lastCompletelyVisibleItemPosition = mLinearLayoutManager.findLastCompletelyVisibleItemPosition();
|
|
|
- if (lastCompletelyVisibleItemPosition == mMessageAdapter.getMessageSize() - 1) {
|
|
|
- isAutoScrollToLast = true;
|
|
|
- } else {
|
|
|
- isAutoScrollToLast = false;
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected ActivityLiveroomLayoutBinding getLayoutView() {
|
|
|
- return ActivityLiveroomLayoutBinding.inflate(getLayoutInflater());
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- @Override
|
|
|
- public void initData() {
|
|
|
- super.initData();
|
|
|
- mRoomId = getIntent().getStringExtra(ROOMID_KEY);
|
|
|
- if (TextUtils.isEmpty(mRoomId)) {
|
|
|
- ToastUtil.getInstance().show(this, "房间id不可为空");
|
|
|
- finish();
|
|
|
- return;
|
|
|
- }
|
|
|
- String userToken = UserHelper.getUserToken();
|
|
|
- if (TextUtils.isEmpty(userToken)) {
|
|
|
- finish();
|
|
|
- return;
|
|
|
- }
|
|
|
- if (LiveConfig.isNeedReInitRTC) {
|
|
|
-
|
|
|
- LiveRTCEngineInitHelper.initRTC();
|
|
|
- }
|
|
|
-
|
|
|
- RCRTCAudioRouteManager.getInstance().init(Utils.getApp());
|
|
|
- mFlLiveView.post(new Runnable() {
|
|
|
- @Override
|
|
|
- public void run() {
|
|
|
- checkVideoViewManager();
|
|
|
- }
|
|
|
- });
|
|
|
- mUserId = UserHelper.getUserId();
|
|
|
- mImUserId = UserHelper.getImUserId();
|
|
|
- mMessageAdapter = new MessageAdapter(this);
|
|
|
- mLinearLayoutManager = new LinearLayoutManager(this, RecyclerView.VERTICAL, false);
|
|
|
- mRecyclerMsg.setLayoutManager(mLinearLayoutManager);
|
|
|
- mRecyclerMsg.setAdapter(mMessageAdapter);
|
|
|
- IMCenter.getInstance().addConnectionStatusListener(connectStatusListener);
|
|
|
- SoftKeyboardUtil.registerSoftInputChangedListener(getWindow(), this);
|
|
|
- mLlMicContainer.setOnEventListener(new LiveRoomMicIconView.OnEventListener() {
|
|
|
- @Override
|
|
|
- public void getUserInfoByUserId(String userId) {
|
|
|
- if (presenter != null) {
|
|
|
- presenter.getUserInfoByUserId(userId);
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- checkNeedPermission();
|
|
|
- }
|
|
|
-
|
|
|
- private void checkNeedPermission() {
|
|
|
- Disposable disposable = new RxPermissions(LiveRoomActivity.this)
|
|
|
- .request(Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE
|
|
|
- , Manifest.permission.CAMERA)
|
|
|
- .subscribe(permission -> {
|
|
|
- if (permission) {
|
|
|
- prepareInitRoom();
|
|
|
- } else {
|
|
|
- CommonConfirmDialog confirmDialog = new CommonConfirmDialog(LiveRoomActivity.this);
|
|
|
- confirmDialog.show();
|
|
|
- confirmDialog.setContent("直播需要麦克风、摄像头、储存权限,去设置?");
|
|
|
- confirmDialog.setConfirmText("去设置");
|
|
|
- confirmDialog.setOnConfirmClickListener(new View.OnClickListener() {
|
|
|
- @Override
|
|
|
- public void onClick(View v) {
|
|
|
- PermissionUtils.toSelfSetting(LiveRoomActivity.this);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- private void prepareInitRoom() {
|
|
|
-
|
|
|
- RongIMClient.ConnectionStatusListener.ConnectionStatus currentConnectionStatus = RongIMClient.getInstance().getCurrentConnectionStatus();
|
|
|
- if (currentConnectionStatus == RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTED) {
|
|
|
- isNeedRefresh = false;
|
|
|
- if (presenter != null) {
|
|
|
- presenter.init(mRoomId, false);
|
|
|
- }
|
|
|
- } else {
|
|
|
-
|
|
|
- isNeedRefresh = true;
|
|
|
- if (presenter != null) {
|
|
|
- presenter.connectIM();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- public void onClick(View view) {
|
|
|
- int id = view.getId();
|
|
|
- if (id == R.id.view_live_status) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (id == R.id.iv_close) {
|
|
|
- if (currentSeatStatus != LiveRoomMsgConstants.MIC_STATUS_NORMAL) {
|
|
|
- showPageCloseOnMicTipDialog();
|
|
|
- } else {
|
|
|
- close();
|
|
|
- }
|
|
|
- }
|
|
|
- if (id == R.id.iv_switch_video_orientation) {
|
|
|
-
|
|
|
- if (mShopCarDialog != null && mShopCarDialog.isShowing()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- changeOrientation();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (id == R.id.iv_switch_video_orientation_full) {
|
|
|
-
|
|
|
- changeOrientation();
|
|
|
- return;
|
|
|
- }
|
|
|
- if (id == R.id.tv_input) {
|
|
|
-
|
|
|
- showInputDialog();
|
|
|
- return;
|
|
|
- }
|
|
|
- if (id == R.id.iv_mic) {
|
|
|
-
|
|
|
- if (!isOnMic()) {
|
|
|
-
|
|
|
- if (checkMicMode()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (connectStatusIsPaused) {
|
|
|
-
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- showMicManagerDialog();
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (id == R.id.icon_add_like) {
|
|
|
- if (checkAddLikeMode()) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- LiveRoomAddLikeHelper.getInstance().handleClick(new LiveRoomAddLikeHelper.OnAddLikeResultCallBack() {
|
|
|
- @Override
|
|
|
- public void onResult(int count) {
|
|
|
- if (presenter != null) {
|
|
|
- presenter.setAddLikeCount(count);
|
|
|
- presenter.handleAction(LiveRoomMsgConstants.ACTION_SEND_ADD_LIKE_COUNT);
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- LiveRoomAnimatorHelper.getInstance().startAddLikeAnimation(LiveRoomActivity.this, content_view);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (id == R.id.view_shop_car_anim) {
|
|
|
- if (liveIsFinish) {
|
|
|
-
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, "直播已结束");
|
|
|
- return;
|
|
|
- }
|
|
|
- if (viewBinding.llShopCarMenu.getVisibility() != View.VISIBLE) {
|
|
|
- viewBinding.llShopCarMenu.setVisibility(View.VISIBLE);
|
|
|
- } else {
|
|
|
- viewBinding.llShopCarMenu.setVisibility(View.GONE);
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (id == R.id.fl_live_view) {
|
|
|
-
|
|
|
- if (isFullScreen()) {
|
|
|
-
|
|
|
- handleHeaderAnim();
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (id == R.id.iv_avatar) {
|
|
|
-
|
|
|
- if (mRoomInfoBean != null) {
|
|
|
- ARouter.getInstance()
|
|
|
- .build(RouterPath.WebCenter.ACTIVITY_HTML)
|
|
|
- .withString(WebConstants.WEB_URL, WebConstants.STUDENT_TEACHER_HOME + mRoomInfoBean.speakerId)
|
|
|
- .navigation();
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (id == R.id.tv_appointment_course || id == R.id.tv_live_course
|
|
|
- || id == R.id.tv_video_course || id == R.id.tv_music_sheet) {
|
|
|
-
|
|
|
- startTeacherHomePage(id);
|
|
|
- sendShowFloatWindowAction(SEND_JUMP_OTHER_PAGE_MSG);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (id == R.id.iv_share) {
|
|
|
-
|
|
|
- showShareDialog();
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void showShareDialog() {
|
|
|
- if (mRoomInfoBean == null) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (mLiveRoomShareDialog == null) {
|
|
|
- mLiveRoomShareDialog = new LiveRoomShareDialog(this);
|
|
|
- mLiveRoomShareDialog.setOnEventListener(new LiveRoomShareDialog.OnEventListener() {
|
|
|
- @Override
|
|
|
- public void onShare(File file) {
|
|
|
- shareLive(file);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- if (!mLiveRoomShareDialog.isShowing()) {
|
|
|
- mLiveRoomShareDialog.show();
|
|
|
- }
|
|
|
- mLiveRoomShareDialog.setData(mRoomInfoBean.speakerPic, mRoomInfoBean.speakerName, mRoomInfoBean.roomUid);
|
|
|
- }
|
|
|
-
|
|
|
- private void showMicManagerDialog() {
|
|
|
- if (mMicManagerDialog == null) {
|
|
|
- mMicManagerDialog = new LiveRoomMicManagerDialog(this);
|
|
|
- mMicManagerDialog.setOnEventListener(new LiveRoomMicManagerDialog.OnEventListener() {
|
|
|
- @Override
|
|
|
- public void onApplyMic() {
|
|
|
-
|
|
|
- handleMicEvent();
|
|
|
- mMicManagerDialog.setMicStatus(LiveRoomMsgConstants.MIC_STATUS_CONNECTING);
|
|
|
- mMicManagerDialog.dismiss();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onApplyCancel() {
|
|
|
-
|
|
|
- handleMicEvent();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onCloseMic() {
|
|
|
-
|
|
|
- handleMicEvent();
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- if (!mMicManagerDialog.isShowing()) {
|
|
|
- mMicManagerDialog.show();
|
|
|
- }
|
|
|
- if (mRoomInfoBean != null) {
|
|
|
- mMicManagerDialog.setLiveTeacherInfo(mRoomInfoBean.speakerPic);
|
|
|
- }
|
|
|
- mMicManagerDialog.setMicStatus(currentSeatStatus);
|
|
|
- }
|
|
|
-
|
|
|
- private void handleMicEvent() {
|
|
|
- if (currentSeatStatus == LiveRoomMsgConstants.MIC_STATUS_NORMAL) {
|
|
|
-
|
|
|
- if (presenter != null) {
|
|
|
- presenter.handleAction(LiveRoomMsgConstants.ACTION_SEND_SEAT_APPLY);
|
|
|
- }
|
|
|
- updateMicIcon(LiveRoomMsgConstants.MIC_STATUS_CONNECTING);
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, getString(R.string.seat_apply_str));
|
|
|
- } else {
|
|
|
-
|
|
|
- showCloseMicTipDialog();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void startTeacherHomePage(int viewId) {
|
|
|
- String params = "";
|
|
|
- if (viewId == R.id.tv_appointment_course) {
|
|
|
- params = "practice";
|
|
|
- }
|
|
|
- if (viewId == R.id.tv_live_course) {
|
|
|
- params = "live";
|
|
|
- }
|
|
|
- if (viewId == R.id.tv_video_course) {
|
|
|
- params = "video";
|
|
|
- }
|
|
|
- if (viewId == R.id.tv_music_sheet) {
|
|
|
- params = "music";
|
|
|
- }
|
|
|
- if (mRoomInfoBean != null) {
|
|
|
- ARouter.getInstance()
|
|
|
- .build(RouterPath.WebCenter.ACTIVITY_HTML)
|
|
|
- .withString(WebConstants.WEB_URL, WebConstants.STUDENT_TEACHER_HOME + mRoomInfoBean.speakerId + "&tabs=" + params)
|
|
|
- .navigation();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private void handleHeaderAnim() {
|
|
|
- float animOffset = cs_header_info.getTranslationY();
|
|
|
- if (animOffset == 0) {
|
|
|
-
|
|
|
- int bottom = cs_header_info.getBottom();
|
|
|
- if (hideHeaderInfoAnim == null) {
|
|
|
- hideHeaderInfoAnim = ObjectAnimator.ofFloat(cs_header_info, "translationY", 0, -bottom);
|
|
|
- hideHeaderInfoAnim.setDuration(500);
|
|
|
- }
|
|
|
- hideHeaderInfoAnim.start();
|
|
|
- } else {
|
|
|
-
|
|
|
- if (showHeaderInfoAnim == null) {
|
|
|
- showHeaderInfoAnim = ObjectAnimator.ofFloat(cs_header_info, "translationY", animOffset, 0);
|
|
|
- showHeaderInfoAnim.setDuration(500);
|
|
|
- }
|
|
|
- showHeaderInfoAnim.start();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void showShopCarDialog() {
|
|
|
- if (mShopCarDialog == null) {
|
|
|
- mShopCarDialog = new LiveRoomShopCarDialog(this);
|
|
|
- mShopCarDialog.setOnEventListener(new LiveRoomShopCarDialog.OnEventListener() {
|
|
|
- @Override
|
|
|
- public void onOpenDetail(String url) {
|
|
|
- mShopCarDialog.dismiss();
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- if (!mShopCarDialog.isShowing()) {
|
|
|
- mShopCarDialog.show();
|
|
|
- }
|
|
|
- mShopCarDialog.setRoomId(mRoomId);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void onResume() {
|
|
|
- super.onResume();
|
|
|
- Log.i("pq", "onResume");
|
|
|
- checkImConnectStatus();
|
|
|
- this.isOnResume = true;
|
|
|
- unbindService();
|
|
|
-
|
|
|
- muteAll(false);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- * 检查IM连接状态
|
|
|
- */
|
|
|
- private void checkImConnectStatus() {
|
|
|
- RongIMClient.ConnectionStatusListener.ConnectionStatus currentConnectionStatus = RongIMClient.getInstance().getCurrentConnectionStatus();
|
|
|
- if (currentConnectionStatus == RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTED) {
|
|
|
- isNeedRefresh = false;
|
|
|
- } else {
|
|
|
- isNeedRefresh = true;
|
|
|
- if (presenter != null) {
|
|
|
- presenter.connectIM();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private void showPageCloseOnMicTipDialog() {
|
|
|
- if (mLiveRoomClosePageOnMicTipDialog == null) {
|
|
|
- mLiveRoomClosePageOnMicTipDialog = new LiveRoomClosePageOnMicTipDialog(this);
|
|
|
- mLiveRoomClosePageOnMicTipDialog.setConfirmClickListener(new View.OnClickListener() {
|
|
|
- @Override
|
|
|
- public void onClick(View v) {
|
|
|
- finish();
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- if (!mLiveRoomClosePageOnMicTipDialog.isShowing()) {
|
|
|
- mLiveRoomClosePageOnMicTipDialog.show();
|
|
|
- }
|
|
|
- mLiveRoomClosePageOnMicTipDialog.setTitle("提示");
|
|
|
- mLiveRoomClosePageOnMicTipDialog.setContent("连麦中,是否退出房间?");
|
|
|
- }
|
|
|
-
|
|
|
- private void showCloseMicTipDialog() {
|
|
|
- if (mRoomCloseMicTipDialog == null) {
|
|
|
- mRoomCloseMicTipDialog = new LiveRoomCloseMicTipDialog(this);
|
|
|
- mRoomCloseMicTipDialog.setConfirmClickListener(new View.OnClickListener() {
|
|
|
- @Override
|
|
|
- public void onClick(View v) {
|
|
|
- handleCloseMicEvent();
|
|
|
- mRoomCloseMicTipDialog.dismiss();
|
|
|
- if (mMicManagerDialog != null) {
|
|
|
- mMicManagerDialog.dismiss();
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- if (!mRoomCloseMicTipDialog.isShowing()) {
|
|
|
- mRoomCloseMicTipDialog.show();
|
|
|
- }
|
|
|
- mRoomCloseMicTipDialog.setContent(getString(currentSeatStatus == LiveRoomMsgConstants.MIC_STATUS_CONNECTING ? R.string.cancel_seat_on_connecting : R.string.cancel_seat_on_connected));
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 取消连麦
|
|
|
- */
|
|
|
- private void handleCloseMicEvent() {
|
|
|
- if (currentSeatStatus == LiveRoomMsgConstants.MIC_STATUS_CONNECTING) {
|
|
|
-
|
|
|
- if (presenter != null) {
|
|
|
- presenter.handleAction(LiveRoomMsgConstants.ACTION_SEND_CANCEL_SEAT_APPLY);
|
|
|
- updateMicIcon(LiveRoomMsgConstants.MIC_STATUS_NORMAL);
|
|
|
- }
|
|
|
- }
|
|
|
- if (currentSeatStatus == LiveRoomMsgConstants.MIC_STATUS_CONNECT_SUCCESS) {
|
|
|
- if (presenter != null) {
|
|
|
- presenter.exitMic();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private boolean isOnApplyMic() {
|
|
|
- return currentSeatStatus == LiveRoomMsgConstants.MIC_STATUS_CONNECTING;
|
|
|
- }
|
|
|
-
|
|
|
- private boolean isOnMic() {
|
|
|
- return currentSeatStatus == LiveRoomMsgConstants.MIC_STATUS_CONNECT_SUCCESS;
|
|
|
- }
|
|
|
-
|
|
|
- private void updateMicIcon(int micStatus) {
|
|
|
-
|
|
|
- iv_mic.post(new Runnable() {
|
|
|
- @Override
|
|
|
- public void run() {
|
|
|
- currentSeatStatus = micStatus;
|
|
|
- if (micStatus == LiveRoomMsgConstants.MIC_STATUS_NORMAL) {
|
|
|
- iv_mic.setImageResource(R.drawable.icon_mic_unconnect);
|
|
|
- }
|
|
|
- if (micStatus == LiveRoomMsgConstants.MIC_STATUS_CONNECTING) {
|
|
|
- iv_mic.setImageResource(R.drawable.icon_mic_connecting);
|
|
|
- }
|
|
|
- if (micStatus == LiveRoomMsgConstants.MIC_STATUS_CONNECT_SUCCESS) {
|
|
|
- iv_mic.setImageResource(R.drawable.icon_mic_conected);
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 检查点赞按钮是否可用
|
|
|
- *
|
|
|
- * @return
|
|
|
- */
|
|
|
- private boolean checkAddLikeMode() {
|
|
|
- if (liveIsFinish) {
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, "直播已结束");
|
|
|
- return true;
|
|
|
- }
|
|
|
- if (connectStatusIsPaused) {
|
|
|
-
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, "网络连接状态异常");
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private void showInputDialog() {
|
|
|
- if (checkChatMode()) return;
|
|
|
- if (mInputBarDialog == null) {
|
|
|
- mInputBarDialog = new InputBarDialog(LiveRoomActivity.this, new InputBar.InputBarListener() {
|
|
|
- @Override
|
|
|
- public boolean onClickSend(String message) {
|
|
|
-
|
|
|
- if (TextUtils.isEmpty(message)) {
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, "消息不能为空");
|
|
|
- return false;
|
|
|
- }
|
|
|
- if (message.length() > LiveConfig.LIVE_MAX_INPUT_TEXT_LENGTH) {
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, "聊天消息需在40个字以内哦");
|
|
|
- return false;
|
|
|
- }
|
|
|
- if (LiveMessageHelper.isQuickAction()) {
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, "您说话太快啦");
|
|
|
- return false;
|
|
|
- }
|
|
|
- sendTextMessage(message);
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean onClickEmoji() {
|
|
|
- return false;
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- mInputBarDialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
|
|
- @Override
|
|
|
- public void onShow(DialogInterface dialog) {
|
|
|
- if (mInputBarDialog != null) {
|
|
|
- mInputBarDialog.showInput();
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- if (!mInputBarDialog.isShowing()) {
|
|
|
- mInputBarDialog.show();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void sendTextMessage(String message) {
|
|
|
- if (checkChatMode()) {
|
|
|
-
|
|
|
- if (mInputBarDialog != null) {
|
|
|
- mInputBarDialog.dismiss();
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
- if (presenter != null) {
|
|
|
- TextMessage textMessage = TextMessage.obtain(message);
|
|
|
- UserInfo userInfo = new UserInfo(mImUserId, UserHelper.getUserName(), null);
|
|
|
- textMessage.setUserInfo(userInfo);
|
|
|
- presenter.sendMessage(textMessage);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void changeOrientation() {
|
|
|
- int currentOrientation = getCurrentOrientation();
|
|
|
- boolean isNeedFullScreen = currentOrientation != Configuration.ORIENTATION_LANDSCAPE;
|
|
|
- if (isNeedFullScreen) {
|
|
|
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
|
|
|
- } else {
|
|
|
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
|
|
- super.onConfigurationChanged(newConfig);
|
|
|
-
|
|
|
- resetVideoContainer(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE);
|
|
|
- }
|
|
|
-
|
|
|
- private void close() {
|
|
|
-
|
|
|
- onBackPressed();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected LiveRoomPresenter createPresenter() {
|
|
|
- return new LiveRoomPresenter();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- * 获取房间信息成功
|
|
|
- *
|
|
|
- * @param roomInfoBean
|
|
|
- */
|
|
|
- @Override
|
|
|
- public void getRoomInfoSuccess(LiveRoomInfoBean roomInfoBean) {
|
|
|
- if (roomInfoBean == null || isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- mRoomInfoBean = roomInfoBean;
|
|
|
- isPcClientLive = TextUtils.equals(mRoomInfoBean.os, "pc");
|
|
|
- resetVideoContainer(!isPcClientLive);
|
|
|
- if (isShowShare()) {
|
|
|
- viewBinding.ivShare.setVisibility(View.VISIBLE);
|
|
|
- } else {
|
|
|
- viewBinding.ivShare.setVisibility(View.GONE);
|
|
|
- }
|
|
|
- currentAddLikeCount = roomInfoBean.likeNum;
|
|
|
-
|
|
|
- isEnableMic = roomInfoBean.whether_mic == 1;
|
|
|
-
|
|
|
- isEnableChat = roomInfoBean.whether_chat == 1;
|
|
|
- if (!TextUtils.isEmpty(roomInfoBean.roomConfig)) {
|
|
|
- try {
|
|
|
- JSONObject jsonObject = new JSONObject(roomInfoBean.roomConfig);
|
|
|
- int chatCtrlMode = jsonObject.optInt("whether_chat", 0);
|
|
|
- int micCtrlMode = jsonObject.optInt("whether_mic", 0);
|
|
|
- isEnableChat = chatCtrlMode == 1;
|
|
|
- isEnableMic = micCtrlMode == 1;
|
|
|
-
|
|
|
- int shopCarMode = jsonObject.optInt("whether_view_shop_cart", 0);
|
|
|
- if (shopCarMode == 1) {
|
|
|
-
|
|
|
- mViewShopCarAnim.setVisibility(View.GONE);
|
|
|
- mIvShopCar.setVisibility(View.GONE);
|
|
|
- } else {
|
|
|
-
|
|
|
- mViewShopCarAnim.setVisibility(View.VISIBLE);
|
|
|
- mIvShopCar.setVisibility(View.INVISIBLE);
|
|
|
- }
|
|
|
- } catch (JSONException e) {
|
|
|
- e.printStackTrace();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- isEnableAll = roomInfoBean.blacklistFlag == 1;
|
|
|
- updateInputTip();
|
|
|
- updateAddLikeCountView();
|
|
|
-
|
|
|
- updateMicIcon(LiveRoomMsgConstants.MIC_STATUS_NORMAL);
|
|
|
- if (mTvNumPeople != null) {
|
|
|
- int peopleCount = roomInfoBean.lookNum + 1;
|
|
|
- mTvNumPeople.setText(String.format("%s人", LiveMemberHelper.getMemberCountText(peopleCount)));
|
|
|
- }
|
|
|
-
|
|
|
- if (mIvAvatar != null) {
|
|
|
-
|
|
|
- Glide.with(LiveRoomActivity.this).load(roomInfoBean.speakerPic).placeholder(R.drawable.icon_teacher_default_head).error(R.drawable.icon_teacher_default_head).into(mIvAvatar);
|
|
|
- }
|
|
|
- if (mTvRoomCreateName != null) {
|
|
|
-
|
|
|
- mTvRoomCreateName.setText(roomInfoBean.speakerName);
|
|
|
- }
|
|
|
-
|
|
|
- if (mMessageAdapter != null) {
|
|
|
- mMessageAdapter.setRoomAuthorId(roomInfoBean.speakerId);
|
|
|
- }
|
|
|
- if (presenter != null) {
|
|
|
-
|
|
|
-
|
|
|
- showLoading();
|
|
|
- LiveEventHelper.getInstance().register(mRoomInfoBean.roomUid);
|
|
|
- presenter.leaveRoom(mRoomInfoBean.roomUid, false, true);
|
|
|
- presenter.joinChartRoom(mRoomInfoBean.roomUid, new IRongCoreCallback.OperationCallback() {
|
|
|
- @Override
|
|
|
- public void onSuccess() {
|
|
|
- Log.i("pq", "加入聊天房间成功");
|
|
|
- if (presenter != null) {
|
|
|
- presenter.handleAction(LiveRoomMsgConstants.ACTION_SEND_JOIN_ROOM);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onError(IRongCoreEnum.CoreErrorCode coreErrorCode) {
|
|
|
- Log.i("pq", "加入聊天房间fail:" + coreErrorCode);
|
|
|
- Toast.makeText(LiveRoomActivity.this, "加入聊天房间fail:" + coreErrorCode, Toast.LENGTH_SHORT).show();
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void updateInputTip() {
|
|
|
- if (tv_input == null) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (isEnableAll) {
|
|
|
- tv_input.setText(getString(R.string.unable_input_tip_str));
|
|
|
- } else {
|
|
|
- tv_input.setText(getString(R.string.live_input_tips_str));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void getRoomInfoError(Throwable t) {
|
|
|
- if (isDestroyed() || isFinishing()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (t instanceof ApiException) {
|
|
|
- ApiException apiException = (ApiException) t;
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, apiException.getErrmsg());
|
|
|
- }
|
|
|
- finish();
|
|
|
- }
|
|
|
-
|
|
|
- private void resetVideoContainer(boolean isFullScreen) {
|
|
|
- if (mVideoViewManager != null) {
|
|
|
- mVideoViewManager.setIsNeedFillScreen(isFullScreen);
|
|
|
- }
|
|
|
- if (isFullScreen) {
|
|
|
- if (isPcClientLive) {
|
|
|
-
|
|
|
- mGroupViews.setVisibility(View.GONE);
|
|
|
- mIvClose.setVisibility(View.GONE);
|
|
|
-
|
|
|
- mFlJoinBarrage.clearAnimation();
|
|
|
- mFlJoinBarrage.setVisibility(View.GONE);
|
|
|
- mFlSnapUpBarrage.clearAnimation();
|
|
|
- mFlSnapUpBarrage.setVisibility(View.GONE);
|
|
|
-
|
|
|
- mIvSwitchVideoOrientation.setVisibility(View.GONE);
|
|
|
- mIvSwitchVideoOrientationFull.setVisibility(View.VISIBLE);
|
|
|
- } else {
|
|
|
-
|
|
|
- mIvSwitchVideoOrientation.setVisibility(View.GONE);
|
|
|
- mIvSwitchVideoOrientationFull.setVisibility(View.GONE);
|
|
|
- }
|
|
|
- ConstraintSet set = new ConstraintSet();
|
|
|
- set.clone(content_view);
|
|
|
- set.clear(mFlLiveView.getId());
|
|
|
- if (!isPcClientLive) {
|
|
|
-
|
|
|
- set.clear(R.id.fl_recycler_container);
|
|
|
- set.connect(R.id.fl_recycler_container, ConstraintSet.TOP, R.id.view_center, ConstraintSet.BOTTOM, SizeUtils.dp2px(50));
|
|
|
- set.connect(R.id.fl_recycler_container, ConstraintSet.LEFT, content_view.getId(), ConstraintSet.LEFT, 0);
|
|
|
- set.connect(R.id.fl_recycler_container, ConstraintSet.RIGHT, content_view.getId(), ConstraintSet.RIGHT, 0);
|
|
|
- set.connect(R.id.fl_recycler_container, ConstraintSet.BOTTOM, tv_input.getId(), ConstraintSet.TOP, SizeUtils.dp2px(10));
|
|
|
- }
|
|
|
- set.connect(mFlLiveView.getId(), ConstraintSet.TOP, content_view.getId(), ConstraintSet.TOP, 0);
|
|
|
- set.connect(mFlLiveView.getId(), ConstraintSet.LEFT, content_view.getId(), ConstraintSet.LEFT, 0);
|
|
|
- set.connect(mFlLiveView.getId(), ConstraintSet.RIGHT, content_view.getId(), ConstraintSet.RIGHT, 0);
|
|
|
- set.connect(mFlLiveView.getId(), ConstraintSet.BOTTOM, content_view.getId(), ConstraintSet.BOTTOM, 0);
|
|
|
- set.applyTo(content_view);
|
|
|
- } else {
|
|
|
-
|
|
|
- if (cs_header_info.getTranslationY() != 0) {
|
|
|
-
|
|
|
- handleHeaderAnim();
|
|
|
- }
|
|
|
- if (isPcClientLive) {
|
|
|
- mGroupViews.setVisibility(View.VISIBLE);
|
|
|
- mIvClose.setVisibility(View.VISIBLE);
|
|
|
- mIvSwitchVideoOrientation.setVisibility(View.VISIBLE);
|
|
|
- mIvSwitchVideoOrientationFull.setVisibility(View.GONE);
|
|
|
- }
|
|
|
-
|
|
|
- ConstraintSet set = new ConstraintSet();
|
|
|
- set.clone(content_view);
|
|
|
- set.clear(mFlLiveView.getId());
|
|
|
- set.connect(mFlLiveView.getId(), ConstraintSet.TOP, cs_header_info.getId(), ConstraintSet.BOTTOM, SizeUtils.dp2px(121));
|
|
|
- set.connect(mFlLiveView.getId(), ConstraintSet.LEFT, content_view.getId(), ConstraintSet.LEFT, 0);
|
|
|
- set.connect(mFlLiveView.getId(), ConstraintSet.RIGHT, content_view.getId(), ConstraintSet.RIGHT, 0);
|
|
|
- int maxHeightAtRatio16_9 = UiUtils.getMaxHeightAtRatio16_9(LiveRoomActivity.this);
|
|
|
- set.constrainHeight(mFlLiveView.getId(), maxHeightAtRatio16_9);
|
|
|
- set.applyTo(content_view);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void changVideoViewSize() {
|
|
|
- int childCount = mFlLiveView.getChildCount();
|
|
|
- if (childCount == 0) {
|
|
|
- return;
|
|
|
- }
|
|
|
- View childAt = mFlLiveView.getChildAt(0);
|
|
|
- if (childAt instanceof RCRTCVideoView) {
|
|
|
- RCRTCVideoView videoView = (RCRTCVideoView) childAt;
|
|
|
-
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void showFinishView() {
|
|
|
-
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- ToastUtil.getInstance().showShort("直播间加入异常,请退出重试");
|
|
|
- Log.i("pq", "加入直播间异常");
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- * 主讲人的音频状态
|
|
|
- *
|
|
|
- * @param isCloseAudioStream
|
|
|
- */
|
|
|
- @Override
|
|
|
- public void syncAudioStatus(boolean isCloseAudioStream) {
|
|
|
- this.creatorIsCloseAudioStream = isCloseAudioStream;
|
|
|
- if (creatorIsCloseAudioStream && creatorIsCloseVideoStream) {
|
|
|
-
|
|
|
- showRestView();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void showRestView() {
|
|
|
- showLiveStatusView(LIVE_STATUS_IS_REST);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void showCloseVideoView() {
|
|
|
-
|
|
|
- creatorIsCloseVideoStream = true;
|
|
|
- if (creatorIsCloseAudioStream) {
|
|
|
-
|
|
|
- showRestView();
|
|
|
- return;
|
|
|
- }
|
|
|
- showLiveStatusView(LIVE_STATUS_IS_CLOSE_VIDEO);
|
|
|
- }
|
|
|
-
|
|
|
- private void showLiveStatusView(int status) {
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (mViewLiveStatus == null) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- mViewLiveStatus.post(new Runnable() {
|
|
|
- @Override
|
|
|
- public void run() {
|
|
|
- hideLoading();
|
|
|
- mViewLiveStatus.setVisibility(View.VISIBLE);
|
|
|
- if (status == LIVE_STATUS_IS_REST) {
|
|
|
- mIvLiveStatusCenterIcon.setImageResource(R.drawable.icon_live_room_rest_bg);
|
|
|
- mTvLiveStatusTipText.setText("休息一下,马上回来!!");
|
|
|
- mTvLiveStatusTipText.setTextSize(12);
|
|
|
- mTvLiveStatusTipText.setBackgroundResource(com.cooleshow.base.R.drawable.shape_gray_14dp);
|
|
|
- } else if (status == LIVE_STATUS_IS_CLOSE_VIDEO) {
|
|
|
- mIvLiveStatusCenterIcon.setImageResource(R.drawable.icon_live_room_close_video);
|
|
|
- mTvLiveStatusTipText.setText("主持人已关闭画面!");
|
|
|
- mTvLiveStatusTipText.setTextSize(12);
|
|
|
- mTvLiveStatusTipText.setBackgroundResource(com.cooleshow.base.R.drawable.shape_gray_14dp);
|
|
|
- } else if (status == LIVE_STATUS_IS_OFFLINE) {
|
|
|
- mIvLiveStatusCenterIcon.setImageResource(R.drawable.icon_live_end_tip);
|
|
|
- mTvLiveStatusTipText.setText("直播已结束!");
|
|
|
- mTvLiveStatusTipText.setTextSize(14);
|
|
|
- mTvLiveStatusTipText.setBackgroundColor(Color.TRANSPARENT);
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void showEmptyStatusView() {
|
|
|
-
|
|
|
- showRestView();
|
|
|
- refreshAudio(null, null);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public View getContentView() {
|
|
|
- return content_view;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void addMessageList(List<MessageContent> messageContents, boolean isReset) {
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void addMessageContent(Message message, boolean isReset) {
|
|
|
-
|
|
|
- Log.i("pq", "收到需要显示msg:" + message);
|
|
|
-
|
|
|
- if (mMessageAdapter != null) {
|
|
|
- mMessageAdapter.addMessage(message);
|
|
|
- if (mRecyclerMsg != null && mLinearLayoutManager != null) {
|
|
|
- mRecyclerMsg.post(new Runnable() {
|
|
|
- @Override
|
|
|
- public void run() {
|
|
|
- if (isAutoScrollToLast) {
|
|
|
- if (mRecyclerMsg != null) {
|
|
|
- mRecyclerMsg.scrollToPosition(mMessageAdapter.getItemCount() - 1);
|
|
|
- }
|
|
|
- } else {
|
|
|
- Log.i("pq", "收到消息,不自动滚动");
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void updateAddLikeCountView() {
|
|
|
- if (mTvAddLikeCount != null) {
|
|
|
- mTvAddLikeCount.setText(getString(R.string.live_room_add_like_count_str, LiveMemberHelper.getStarsCountText(currentAddLikeCount)));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 控制聊天模式
|
|
|
- *
|
|
|
- * @param isEnableChat
|
|
|
- */
|
|
|
- @Override
|
|
|
- public void switchChatMode(boolean isEnableChat) {
|
|
|
-
|
|
|
- Log.i("pq", "聊天禁止:" + isEnableChat);
|
|
|
- this.isEnableChat = isEnableChat;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void liveRoomOffline() {
|
|
|
-
|
|
|
- ToastUtil.getInstance().show(this, "直播已结束");
|
|
|
- this.liveIsFinish = true;
|
|
|
-
|
|
|
- if (mMicManagerDialog != null) {
|
|
|
- mMicManagerDialog.dismiss();
|
|
|
- }
|
|
|
- handleCloseMicEvent();
|
|
|
- notifyMicDelAll();
|
|
|
- showLiveStatusView(LIVE_STATUS_IS_OFFLINE);
|
|
|
- hideDialog();
|
|
|
-
|
|
|
- LiveEventHelper.getInstance().leaveRoom(null);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void liveGoodsChange(String jsonStr) {
|
|
|
-
|
|
|
- if (isDestroyed() || isFinishing()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (mShopCarDialog != null) {
|
|
|
- mShopCarDialog.changeGoods(jsonStr);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 关闭一些交互弹窗
|
|
|
- */
|
|
|
- private void hideDialog() {
|
|
|
-
|
|
|
- if (mInviteSeatMicTipDialog != null && mInviteSeatMicTipDialog.isShowing()) {
|
|
|
- mInviteSeatMicTipDialog.dismiss();
|
|
|
- }
|
|
|
-
|
|
|
- if (mShopCarDialog != null && mShopCarDialog.isShowing()) {
|
|
|
- mShopCarDialog.dismiss();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * @param rcUserSeatResponseMessage
|
|
|
- */
|
|
|
- @Override
|
|
|
- public void seatResponse(RCUserSeatResponseMessage rcUserSeatResponseMessage) {
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (rcUserSeatResponseMessage == null || !isOwn(rcUserSeatResponseMessage.getAudienceId())) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (rcUserSeatResponseMessage != null) {
|
|
|
- int type = rcUserSeatResponseMessage.getType();
|
|
|
- if (type == LiveRoomMsgConstants.MIC_RESPONSE_AGREE) {
|
|
|
-
|
|
|
- if (presenter != null) {
|
|
|
- if (isOnApplyMic()) {
|
|
|
- presenter.joinMic();
|
|
|
- }
|
|
|
- }
|
|
|
- return;
|
|
|
- }
|
|
|
- if (type == LiveRoomMsgConstants.MIC_RESPONSE_DISAGREE) {
|
|
|
-
|
|
|
- updateMicIcon(LiveRoomMsgConstants.MIC_STATUS_NORMAL);
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, getString(R.string.create_refuse_seat_tip));
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onSwitchRole(String userId, RCRTCLiveRole role) {
|
|
|
- Log.i("pq", "onSwitchRole:" + userId);
|
|
|
- if (role.getType() == RCRTCLiveRole.AUDIENCE.getType()) {
|
|
|
-
|
|
|
- notifyMicContainerDel(userId);
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onUserJoinRoom(RCRTCRemoteUser rcrtcRemoteUser) {
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- Log.i("pq", "onUserJoinRoom:" + rcrtcRemoteUser.getUserId());
|
|
|
- if (TextUtils.equals(rcrtcRemoteUser.getUserId(), createRoomId)) {
|
|
|
- createRoomId = "";
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onUserLeftRoomMic(RCRTCRemoteUser rcrtcRemoteUser) {
|
|
|
- Log.i("pq", "onUserLeftRoomMic:" + rcrtcRemoteUser.getUserId());
|
|
|
- if (rcrtcRemoteUser != null) {
|
|
|
- notifyMicContainerDel(rcrtcRemoteUser.getUserId());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onUserOfflineRoomMic(RCRTCRemoteUser rcrtcRemoteUser) {
|
|
|
- Log.i("pq", "onUserOfflineRoomMic:" + rcrtcRemoteUser.getUserId());
|
|
|
- if (rcrtcRemoteUser != null) {
|
|
|
- notifyMicContainerDel(rcrtcRemoteUser.getUserId());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onRemoteUserPublishResource(String remoteUserId) {
|
|
|
- if (!TextUtils.isEmpty(remoteUserId)) {
|
|
|
- Log.i("pq", "收到onRemoteUserPublishResource:" + remoteUserId);
|
|
|
- refreshAudio(null, null);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onRefuseAllMicApply() {
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (mMicManagerDialog != null) {
|
|
|
- mMicManagerDialog.dismiss();
|
|
|
- }
|
|
|
- if (isOnApplyMic()) {
|
|
|
- updateMicIcon(LiveRoomMsgConstants.MIC_STATUS_NORMAL);
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, getString(R.string.create_refuse_seat_tip));
|
|
|
- buildResponseMessage();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 因全部拒绝连麦消息没有携带id相关参数,所以本地造一个拒绝连麦的消息插入到消息列表
|
|
|
- */
|
|
|
- private void buildResponseMessage() {
|
|
|
- if (mRoomInfoBean != null) {
|
|
|
- String userId = UserHelper.getImUserId();
|
|
|
- RCUserSeatResponseMessage responseMessage = new RCUserSeatResponseMessage();
|
|
|
- responseMessage.setType(LiveRoomMsgConstants.MIC_RESPONSE_DISAGREE);
|
|
|
- responseMessage.setTeacherId(mRoomInfoBean.speakerId);
|
|
|
- responseMessage.setTeacherName(mRoomInfoBean.speakerName);
|
|
|
- responseMessage.setAudienceId(UserHelper.getImUserId());
|
|
|
- responseMessage.setAudienceName(UserHelper.getUserName());
|
|
|
- Message message = Message.obtain(userId, Conversation.ConversationType.CHATROOM, responseMessage);
|
|
|
- message.setObjectName(LiveRoomMsgConstants.TAG_CHAT_ROOM_SEAT_RESPONSE);
|
|
|
- addMessageContent(message, false);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onUnderAllMic() {
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (isOnMic()) {
|
|
|
- if (presenter != null) {
|
|
|
- presenter.exitMic();
|
|
|
- }
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, getString(R.string.down_mic_by_create_tip));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void syncMemberCount(String count) {
|
|
|
-
|
|
|
- Log.i("pq", "syncMemberCount" + count);
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (mTvNumPeople != null) {
|
|
|
- mTvNumPeople.setText(String.format("%s人", LiveMemberHelper.getMemberCountText(count)));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onUserKickOutMsg(RCUserKickOutMessage userKickOutMessage) {
|
|
|
-
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (userKickOutMessage == null || !isOwn(userKickOutMessage.getTargetId())) {
|
|
|
- return;
|
|
|
- }
|
|
|
- String targetId = userKickOutMessage.getTargetId();
|
|
|
- Log.i("pq", "receive kickOut msg targetId:" + targetId);
|
|
|
- finish();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 控制连麦模式
|
|
|
- *
|
|
|
- * @param isEnableMic
|
|
|
- */
|
|
|
- @Override
|
|
|
- public void changeMicControlMode(boolean isEnableMic) {
|
|
|
-
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- Log.i("pq", "receive mic mode control msg 禁麦模式:" + isEnableMic);
|
|
|
- this.isEnableMic = isEnableMic;
|
|
|
- if (isEnableMic && isOnApplyMic()) {
|
|
|
- handleCloseMicEvent();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void refresh() {
|
|
|
-
|
|
|
- List<RCRTCVideoOutputStream> outputStreams = new ArrayList<>();
|
|
|
- List<RCRTCVideoInputStream> videoInputStreams = new ArrayList<>();
|
|
|
-
|
|
|
- List<RCRTCAudioInputStream> audioinputstream = new ArrayList<>();
|
|
|
- List<RCRTCAudioOutputStream> audioOutputStreams = new ArrayList<>();
|
|
|
- presenter.getAllStreams(outputStreams, videoInputStreams, audioinputstream, audioOutputStreams);
|
|
|
- updateVideoView(outputStreams, videoInputStreams, audioinputstream, audioOutputStreams);
|
|
|
- refreshAudio(audioinputstream, audioOutputStreams);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void subscribeAVStreamSuccess(List<RCRTCInputStream> inputStreams) {
|
|
|
- if (isDestroyed() || isFinishing()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (liveIsFinish) {
|
|
|
-
|
|
|
- return;
|
|
|
- }
|
|
|
- this.liveIsPause = false;
|
|
|
- if (inputStreams != null) {
|
|
|
- refresh();
|
|
|
- } else {
|
|
|
- showEmptyStatusView();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onSubscribeFailed() {
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void setRoomData(LiveRoomInfoBean roomInfoBean) {
|
|
|
-
|
|
|
- if (presenter != null) {
|
|
|
-
|
|
|
- presenter.notifyJoinRoomAction(mRoomId, mUserId);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 清除所有连麦用户的UI
|
|
|
- */
|
|
|
- private void notifyMicDelAll() {
|
|
|
- if (mLlMicContainer != null) {
|
|
|
- mLlMicContainer.post(new Runnable() {
|
|
|
- @Override
|
|
|
- public void run() {
|
|
|
- mLlMicContainer.delAll();
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void notifyMicContainerDel(String userId) {
|
|
|
- mLlMicContainer.post(new Runnable() {
|
|
|
- @Override
|
|
|
- public void run() {
|
|
|
- mLlMicContainer.deleteMicUser(new UserInfo(userId, "", null));
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 连麦成功-身份切换主播成功
|
|
|
- */
|
|
|
- @Override
|
|
|
- public void onSeatMicSuccess() {
|
|
|
- updateMicIcon(LiveRoomMsgConstants.MIC_STATUS_CONNECT_SUCCESS);
|
|
|
- if (mMicManagerDialog != null) {
|
|
|
- mMicManagerDialog.dismiss();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 连麦失败
|
|
|
- */
|
|
|
- @Override
|
|
|
- public void onSeatMicFail() {
|
|
|
- updateMicIcon(LiveRoomMsgConstants.MIC_STATUS_NORMAL);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * \
|
|
|
- * 退出连麦成功
|
|
|
- */
|
|
|
- @Override
|
|
|
- public void onExitSeatMicSuccess() {
|
|
|
- updateMicIcon(LiveRoomMsgConstants.MIC_STATUS_NORMAL);
|
|
|
- if (!TextUtils.isEmpty(mImUserId)) {
|
|
|
- notifyMicContainerDel(mImUserId);
|
|
|
- }
|
|
|
- if (presenter != null) {
|
|
|
- presenter.handleAction(LiveRoomMsgConstants.ACTION_SEND_DOWN_SEAT_MIC);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 主播邀请上麦
|
|
|
- *
|
|
|
- * @param seatApplyMessage
|
|
|
- */
|
|
|
- @Override
|
|
|
- public void onInviteSeatMic(RCUserSeatApplyMessage seatApplyMessage) {
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (seatApplyMessage == null || !isOwn(seatApplyMessage.getAudienceId())) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (seatApplyMessage.getType() == LiveRoomMsgConstants.MIC_ACTION_INVITE_SEAT_BY_CREATE) {
|
|
|
-
|
|
|
- showInviteSeatDialog();
|
|
|
- return;
|
|
|
-
|
|
|
- }
|
|
|
- if (seatApplyMessage.getType() == LiveRoomMsgConstants.MIC_ACTION_CANCEL_INVITE_SEAT_BY_CREATE) {
|
|
|
-
|
|
|
- if (mInviteSeatMicTipDialog != null) {
|
|
|
- mInviteSeatMicTipDialog.dismiss();
|
|
|
- }
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, getString(R.string.create_recall_seat_invite_tip));
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- if (seatApplyMessage.getType() == LiveRoomMsgConstants.MIC_ACTION_CANCEL_SEAT_BY_CREATE) {
|
|
|
-
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, getString(R.string.down_mic_by_create_tip));
|
|
|
- if (presenter != null) {
|
|
|
- presenter.exitMic();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onUserUnusualLogout(RCUserLogOutUnusualMessage loginOutUnusualMessage) {
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (loginOutUnusualMessage == null || !isOwn(loginOutUnusualMessage.getUserId())) {
|
|
|
- return;
|
|
|
- }
|
|
|
- Log.i("pq", "unusual logout");
|
|
|
- finish();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 暂停直播,进行下麦处理
|
|
|
- */
|
|
|
- @Override
|
|
|
- public void onLivePause() {
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- this.liveIsPause = true;
|
|
|
- ToastUtil.getInstance().show(this, getString(R.string.live_is_pause_str));
|
|
|
- handleCloseMicEvent();
|
|
|
- if (mMicManagerDialog != null) {
|
|
|
- mMicManagerDialog.dismiss();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 点赞数量同步
|
|
|
- *
|
|
|
- * @param addLikeMessage
|
|
|
- */
|
|
|
- @Override
|
|
|
- public void onAddLikeMessage(Message message, RCUserSyncAddLikeCountMessage addLikeMessage) {
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (TextUtils.equals(message.getSenderUserId(), mImUserId)) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (addLikeMessage != null) {
|
|
|
- if (mTvAddLikeCount != null && addLikeMessage != null) {
|
|
|
-
|
|
|
-
|
|
|
- currentAddLikeCount = addLikeMessage.getCount();
|
|
|
- updateAddLikeCountView();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 获取上麦用户信息成功
|
|
|
- *
|
|
|
- * @param friendInfoBean
|
|
|
- */
|
|
|
- @Override
|
|
|
- public void getFriendInfoSuccess(FriendInfoBean friendInfoBean) {
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (mLlMicContainer != null && friendInfoBean != null) {
|
|
|
- mLlMicContainer.updateTargetUserInfo(new UserInfo(friendInfoBean.imFriendId, friendInfoBean.friendNickname, Uri.parse(friendInfoBean.friendAvatar)));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onPublishSuccess() {
|
|
|
- refresh();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- * 黑名单状态切换
|
|
|
- *
|
|
|
- * @param userId
|
|
|
- * @param isAddBlack
|
|
|
- */
|
|
|
- @Override
|
|
|
- public void changeBlackUserStatus(String userId, boolean isAddBlack) {
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (isOwn(userId)) {
|
|
|
- isEnableAll = isAddBlack;
|
|
|
- if (isEnableAll) {
|
|
|
-
|
|
|
- if (currentSeatStatus != LiveRoomMsgConstants.MIC_STATUS_NORMAL) {
|
|
|
- handleCloseMicEvent();
|
|
|
- }
|
|
|
- }
|
|
|
- updateInputTip();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private boolean isOwn(String targetUserId) {
|
|
|
- return TextUtils.equals(mImUserId, targetUserId);
|
|
|
- }
|
|
|
-
|
|
|
- private void showInviteSeatDialog() {
|
|
|
- if (mInviteSeatMicTipDialog == null) {
|
|
|
- mInviteSeatMicTipDialog = new LiveRoomInviteSeatMicTipDialog(this);
|
|
|
- mInviteSeatMicTipDialog.setViewClickListener(new View.OnClickListener() {
|
|
|
- @Override
|
|
|
- public void onClick(View v) {
|
|
|
- if (v.getId() == R.id.tv_sure) {
|
|
|
-
|
|
|
- if (presenter != null) {
|
|
|
- presenter.handleAction(LiveRoomMsgConstants.ACTION_SEND_CANCEL_SEAT_AGREE_RESPONSE);
|
|
|
- presenter.joinMic();
|
|
|
- }
|
|
|
- }
|
|
|
- if (v.getId() == R.id.tv_cancel) {
|
|
|
-
|
|
|
- if (presenter != null) {
|
|
|
- presenter.handleAction(LiveRoomMsgConstants.ACTION_SEND_CANCEL_SEAT_DISAGREE_RESPONSE);
|
|
|
- }
|
|
|
-
|
|
|
- handleCloseMicEvent();
|
|
|
- }
|
|
|
- mInviteSeatMicTipDialog.dismiss();
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- if (!mInviteSeatMicTipDialog.isShowing()) {
|
|
|
- mInviteSeatMicTipDialog.show();
|
|
|
- }
|
|
|
- String defaultNick = "主讲人";
|
|
|
- if (mRoomInfoBean != null && !TextUtils.isEmpty(mRoomInfoBean.speakerName)) {
|
|
|
- defaultNick = mRoomInfoBean.speakerName;
|
|
|
- }
|
|
|
- mInviteSeatMicTipDialog.setContent(defaultNick);
|
|
|
- }
|
|
|
-
|
|
|
- private void refreshAudio(List<RCRTCAudioInputStream> audioInputStreams, List<RCRTCAudioOutputStream> audioOutputStreams) {
|
|
|
- if (audioInputStreams == null || audioOutputStreams == null) {
|
|
|
-
|
|
|
- audioInputStreams = new ArrayList<>();
|
|
|
- audioOutputStreams = new ArrayList<>();
|
|
|
- presenter.getAudioStreams(audioInputStreams, audioOutputStreams);
|
|
|
- }
|
|
|
- ArrayList<String> onMicUserIds = new ArrayList<>();
|
|
|
-
|
|
|
- for (int i = 0; i < audioOutputStreams.size(); i++) {
|
|
|
- RCRTCAudioOutputStream audioInputStream = audioOutputStreams.get(i);
|
|
|
- if (audioInputStream.getMediaType() == RCRTCMediaType.AUDIO) {
|
|
|
-
|
|
|
- String userId = audioInputStream.getUserId();
|
|
|
- if (mRoomInfoBean != null) {
|
|
|
- if (!TextUtils.equals(mRoomInfoBean.speakerId, userId)) {
|
|
|
- onMicUserIds.add(userId);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- for (int i = 0; i < audioInputStreams.size(); i++) {
|
|
|
- RCRTCAudioInputStream rcrtcAudioInputStream = audioInputStreams.get(i);
|
|
|
- if (rcrtcAudioInputStream.getMediaType() == RCRTCMediaType.AUDIO) {
|
|
|
-
|
|
|
- String userId = rcrtcAudioInputStream.getUserId();
|
|
|
- if (mRoomInfoBean != null) {
|
|
|
- if (!TextUtils.equals(mRoomInfoBean.speakerId, userId)) {
|
|
|
- onMicUserIds.add(userId);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- mLlMicContainer.refreshUI(onMicUserIds);
|
|
|
- }
|
|
|
-
|
|
|
- public void updateVideoView(List<RCRTCVideoOutputStream> outputStreams, List<RCRTCVideoInputStream> inputStreams, List<RCRTCAudioInputStream> audioInputStreams, List<RCRTCAudioOutputStream> audioOutputStreams) {
|
|
|
- mFlLiveView.post(new Runnable() {
|
|
|
- @Override
|
|
|
- public void run() {
|
|
|
- ArrayList<RCRTCVideoStream> videoStreams = new ArrayList<RCRTCVideoStream>();
|
|
|
- videoStreams.addAll(outputStreams);
|
|
|
- videoStreams.addAll(inputStreams);
|
|
|
- ArrayList<RCRTCVideoView> list = new ArrayList<>();
|
|
|
- boolean isNormalCreateAudioStatus = true;
|
|
|
-
|
|
|
- for (int i = 0; i < videoStreams.size(); i++) {
|
|
|
- RCRTCVideoStream rcrtcVideoStream = videoStreams.get(i);
|
|
|
- RCRTCVideoView rongRTCVideoView = new RCRTCVideoView(LiveRoomActivity.this);
|
|
|
- rcrtcVideoStream.setVideoView(rongRTCVideoView);
|
|
|
- list.add(rongRTCVideoView);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- for (int i = 0; i < audioInputStreams.size(); i++) {
|
|
|
- RCRTCAudioInputStream audioInputStream = audioInputStreams.get(i);
|
|
|
- if (audioInputStream.getMediaType() == RCRTCMediaType.AUDIO) {
|
|
|
-
|
|
|
- String userId = audioInputStream.getUserId();
|
|
|
- if (mRoomInfoBean != null) {
|
|
|
- if (TextUtils.equals(mRoomInfoBean.speakerId, userId)) {
|
|
|
-
|
|
|
- RCRTCResourceState resourceState = audioInputStream.getResourceState();
|
|
|
- isNormalCreateAudioStatus = resourceState == RCRTCResourceState.NORMAL;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- if (list.size() != 0) {
|
|
|
- creatorIsCloseVideoStream = false;
|
|
|
- mViewLiveStatus.setVisibility(View.GONE);
|
|
|
- checkVideoViewManager();
|
|
|
- mVideoViewManager.update(list, isPcClientLive, true);
|
|
|
- } else {
|
|
|
- creatorIsCloseVideoStream = true;
|
|
|
- if (isNormalCreateAudioStatus) {
|
|
|
- showCloseVideoView();
|
|
|
- } else {
|
|
|
-
|
|
|
- showRestView();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 检查VideoViewManager是否未null;
|
|
|
- */
|
|
|
- private void checkVideoViewManager() {
|
|
|
- if (mVideoViewManager == null) {
|
|
|
- mVideoViewManager = new VideoViewManager(mFlLiveView, mFlLiveView.getWidth(), mFlLiveView.getHeight());
|
|
|
- mVideoViewManager.setRetryClickListener(new View.OnClickListener() {
|
|
|
- @Override
|
|
|
- public void onClick(View v) {
|
|
|
- if (presenter != null) {
|
|
|
-
|
|
|
- presenter.subscribeAVStream();
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private boolean findMainScreenStreams(String streamUserId, String streamTag) {
|
|
|
- if (TextUtils.equals("screenshare", streamTag)) {
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
- if (TextUtils.equals(mRoomInfoBean.speakerId, streamUserId)) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void onSaveInstanceState(Bundle outState) {
|
|
|
- super.onSaveInstanceState(outState);
|
|
|
- Log.i("LiveRoomActivity", "onSaveInstanceState");
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onBackPressed() {
|
|
|
- if (liveIsFinish) {
|
|
|
- super.onBackPressed();
|
|
|
- return;
|
|
|
- }
|
|
|
- boolean permissionOverlay = FloatWindowHelper.requestOverlayPermission(this);
|
|
|
- if (!permissionOverlay) {
|
|
|
- showOpenOverlayPermissionTipDialog();
|
|
|
- } else {
|
|
|
- if (isFullScreen()) {
|
|
|
- changeOrientation();
|
|
|
- } else {
|
|
|
- showExitLiveTipDialog();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 退出直播间提示弹窗
|
|
|
- */
|
|
|
- private void showExitLiveTipDialog() {
|
|
|
- LiveRoomCloseTipDialog closeTipDialog = new LiveRoomCloseTipDialog(this);
|
|
|
- closeTipDialog.setConfirmClickListener(new View.OnClickListener() {
|
|
|
- @Override
|
|
|
- public void onClick(View v) {
|
|
|
- finish();
|
|
|
- }
|
|
|
- });
|
|
|
- closeTipDialog.show();
|
|
|
- }
|
|
|
-
|
|
|
- private void showOpenOverlayPermissionTipDialog() {
|
|
|
- OpenOverlayPermissionTipDialog overlayPermissionTipDialog = new OpenOverlayPermissionTipDialog(this);
|
|
|
- overlayPermissionTipDialog.setConfirmClickListener(new View.OnClickListener() {
|
|
|
- @Override
|
|
|
- public void onClick(View v) {
|
|
|
- overlayPermissionTipDialog.dismiss();
|
|
|
- Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
|
|
|
- startActivityForResult(intent, FloatWindowHelper.REQUEST_OVERLAY_CODE);
|
|
|
- }
|
|
|
- });
|
|
|
- overlayPermissionTipDialog.show();
|
|
|
- overlayPermissionTipDialog.setCancelClickListener(new View.OnClickListener() {
|
|
|
- @Override
|
|
|
- public void onClick(View v) {
|
|
|
- overlayPermissionTipDialog.dismiss();
|
|
|
- finish();
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
|
|
- super.onRestoreInstanceState(savedInstanceState);
|
|
|
-
|
|
|
- Log.i("LiveRoomActivity", "onRestoreInstanceState");
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private int getCurrentOrientation() {
|
|
|
- return getResources().getConfiguration().orientation;
|
|
|
- }
|
|
|
-
|
|
|
- private boolean isFullScreen() {
|
|
|
- return getCurrentOrientation() == Configuration.ORIENTATION_LANDSCAPE;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private void startFloatWindowService() {
|
|
|
- boolean hasPermission = FloatWindowHelper.requestOverlayPermission(this);
|
|
|
- if (hasPermission) {
|
|
|
- if (mVideoViewManager != null && mVideoViewManager.getAllVideoViews() != null && mVideoViewManager.getAllVideoViews().size() > 0) {
|
|
|
- Intent intent = new Intent(this, FloatingWindowService.class);
|
|
|
- initServiceConnection();
|
|
|
- intent.putExtra("isPc", isPcClientLive);
|
|
|
- bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void initServiceConnection() {
|
|
|
- if (serviceConnection == null) {
|
|
|
- serviceConnection = new ServiceConnection() {
|
|
|
- @Override
|
|
|
- public void onServiceConnected(ComponentName name, IBinder service) {
|
|
|
- if (service instanceof FloatingWindowService.MyBinder) {
|
|
|
- FloatingWindowService.MyBinder mBinder = (FloatingWindowService.MyBinder) service;
|
|
|
- if (mVideoViewManager != null) {
|
|
|
- ArrayList<RCRTCVideoView> allVideoViews = mVideoViewManager.getAllVideoViews();
|
|
|
- mBinder.addVideoView(allVideoViews);
|
|
|
- }
|
|
|
- mBinder.setOnEventListener(new FloatingWindowService.OnEventListener() {
|
|
|
- @Override
|
|
|
- public void onServiceDestroy() {
|
|
|
- if (mVideoViewManager != null) {
|
|
|
- mVideoViewManager.update(mVideoViewManager.getAllVideoViews(), isPcClientLive, false);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onUnbindService() {
|
|
|
- muteAll(true);
|
|
|
- unbindService();
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onServiceDisconnected(ComponentName name) {
|
|
|
- if (mVideoViewManager != null) {
|
|
|
- mVideoViewManager.update(mVideoViewManager.getAllVideoViews(), isPcClientLive, false);
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 是否静音全部
|
|
|
- *
|
|
|
- * @param isMute
|
|
|
- */
|
|
|
- private void muteAll(boolean isMute) {
|
|
|
- if (LiveEventHelper.getInstance().getRtcRoom() != null) {
|
|
|
- RCRTCRoom rtcRoom = LiveEventHelper.getInstance().getRtcRoom();
|
|
|
- if (rtcRoom.getLocalUser() != null) {
|
|
|
- LiveEventHelper.getInstance().getRtcRoom().muteAllRemoteAudio(isMute);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
|
|
- super.onActivityResult(requestCode, resultCode, data);
|
|
|
- if (resultCode == RESULT_OK) {
|
|
|
- if (requestCode == SHARE_CHAT_REQUEST_CODE) {
|
|
|
-
|
|
|
- if (targetShareImgFile == null) {
|
|
|
- ToastUtil.getInstance().showShort("分享失败");
|
|
|
- return;
|
|
|
- }
|
|
|
- ShareHelper.parseShareContactData(targetShareImgFile, data);
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- * 销毁悬浮窗service
|
|
|
- */
|
|
|
- private void unbindService() {
|
|
|
- if (serviceConnection != null) {
|
|
|
- try {
|
|
|
- boolean isExist = AppUtils.isServiceWork(this, FloatingWindowService.CLASS_PATH);
|
|
|
- if (isExist) {
|
|
|
- unbindService(serviceConnection);
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- e.printStackTrace();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void onPause() {
|
|
|
- super.onPause();
|
|
|
- isOnResume = false;
|
|
|
- sendShowFloatWindowAction(SEND_APP_BACKGROUND_MSG);
|
|
|
- }
|
|
|
-
|
|
|
- private void sendShowFloatWindowAction(int what) {
|
|
|
- android.os.Message message = android.os.Message.obtain();
|
|
|
- message.what = what;
|
|
|
- mHandler.sendMessageDelayed(message, SEND_SHOW_FLOAT_WINDOW_TIME);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 检查聊天模式
|
|
|
- */
|
|
|
- public boolean checkChatMode() {
|
|
|
- if (liveIsFinish) {
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, "直播已结束");
|
|
|
- return true;
|
|
|
- }
|
|
|
- if (isEnableAll) {
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, "您已被管理员禁言");
|
|
|
- return true;
|
|
|
- }
|
|
|
- if (isEnableChat) {
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, "禁止聊天模式");
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 检查mic模式
|
|
|
- */
|
|
|
- public boolean checkMicMode() {
|
|
|
- if (liveIsPause) {
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, "老师暂停中,暂时无法接受邀请");
|
|
|
- return true;
|
|
|
- }
|
|
|
- if (liveIsFinish) {
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, "直播已结束");
|
|
|
- return true;
|
|
|
- }
|
|
|
- if (isEnableAll) {
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, "您已被管理员禁言");
|
|
|
- return true;
|
|
|
- }
|
|
|
- if (isEnableMic) {
|
|
|
- ToastUtil.getInstance().show(LiveRoomActivity.this, getString(R.string.enable_mic_mode_tip));
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void receiveJoinMessage(RCChatJoinRoomMessage joinRoomMessage) {
|
|
|
-
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (joinRoomMessage == null) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (isCanShowBarrage()) {
|
|
|
- mTvJoinBarrage.setText(getString(R.string.enter_live_tip_str, LiveMemberHelper.getMessageName(joinRoomMessage)));
|
|
|
- showBarrageViewAnim(mFlJoinBarrage);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private void resetMicStatus(RCChatJoinRoomMessage joinRoomMessage) {
|
|
|
- if (!isOnApplyMic()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- String joinUserId = joinRoomMessage.getUserId();
|
|
|
- if (TextUtils.isEmpty(joinUserId)) {
|
|
|
- if (joinRoomMessage.getUserInfo() != null) {
|
|
|
- joinUserId = joinRoomMessage.getUserInfo().getUserId();
|
|
|
- }
|
|
|
- }
|
|
|
- if (!TextUtils.isEmpty(joinUserId) && mRoomInfoBean != null) {
|
|
|
- if (TextUtils.equals(joinUserId, mRoomInfoBean.speakerId)) {
|
|
|
-
|
|
|
- if (isOnApplyMic()) {
|
|
|
- updateMicIcon(LiveRoomMsgConstants.MIC_STATUS_NORMAL);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 显示弹幕消息
|
|
|
- */
|
|
|
- private void showBarrageViewAnim(View targetView) {
|
|
|
- if (mFlJoinBarrage.getVisibility() == View.VISIBLE || mFlSnapUpBarrage.getVisibility() == View.VISIBLE) {
|
|
|
- return;
|
|
|
- }
|
|
|
- LiveRoomAnimatorHelper.getInstance().startBarrageViewAnimation(this, targetView);
|
|
|
- hideBarrageView();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 隐藏弹幕消息
|
|
|
- */
|
|
|
- private void hideBarrageView() {
|
|
|
- mHandler.postDelayed(new Runnable() {
|
|
|
- @Override
|
|
|
- public void run() {
|
|
|
- if (mFlJoinBarrage != null) {
|
|
|
- mFlJoinBarrage.setVisibility(View.GONE);
|
|
|
- }
|
|
|
- if (mFlSnapUpBarrage != null) {
|
|
|
- mFlSnapUpBarrage.setVisibility(View.GONE);
|
|
|
- }
|
|
|
- }
|
|
|
- }, LiveConfig.LIVE_HIDE_BARRAGE_VIEW_TIME);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void receiveSnapUpMessage(RCOnSnappingUpMessage onSnappingUpMessage) {
|
|
|
-
|
|
|
- if (isFinishing() || isDestroyed()) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (onSnappingUpMessage == null) {
|
|
|
- return;
|
|
|
- }
|
|
|
- if (isCanShowBarrage()) {
|
|
|
- mTvSnapUpBarrage.setText(getString(R.string.live_snap_up_tip_str, LiveMemberHelper.getMessageName(onSnappingUpMessage)));
|
|
|
- showBarrageViewAnim(mFlSnapUpBarrage);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * 判断是否显示弹幕消息
|
|
|
- *
|
|
|
- * @return
|
|
|
- */
|
|
|
- private boolean isCanShowBarrage() {
|
|
|
- if (isFullScreen()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- return mFlJoinBarrage.getVisibility() != View.VISIBLE && mFlSnapUpBarrage.getVisibility() != View.VISIBLE;
|
|
|
- }
|
|
|
-
|
|
|
- private void shareLive(File imgfile) {
|
|
|
- if (mLiveRoomShareDialog != null && mLiveRoomShareDialog.isShowing()) {
|
|
|
- mLiveRoomShareDialog.dismiss();
|
|
|
- }
|
|
|
- this.targetShareImgFile = imgfile;
|
|
|
- ShareAction ShareAction = new ShareAction(this).setDisplayList(
|
|
|
- SHARE_MEDIA.WEIXIN, SHARE_MEDIA.WEIXIN_CIRCLE, SHARE_MEDIA.SINA)
|
|
|
- .setShareboardclickCallback(new ShareBoardlistener() {
|
|
|
- @Override
|
|
|
- public void onclick(SnsPlatform snsPlatform, SHARE_MEDIA share_media) {
|
|
|
-
|
|
|
- if (share_media == null) {
|
|
|
- if (TextUtils.equals(snsPlatform.mKeyword, "chat")) {
|
|
|
-
|
|
|
- ARouter.getInstance().build(TCChatRouterPath.CHAT_SELECT_CONTACT)
|
|
|
- .navigation(LiveRoomActivity.this, SHARE_CHAT_REQUEST_CODE);
|
|
|
- }
|
|
|
- } else {
|
|
|
- if (!UMShareAPI.get(getApplicationContext()).isInstall(LiveRoomActivity.this, share_media)) {
|
|
|
- ToastUtil.getInstance().show(getApplicationContext(), "应用未安装,分享失败");
|
|
|
- return;
|
|
|
- }
|
|
|
- UMImage image = new UMImage(LiveRoomActivity.this, imgfile);
|
|
|
- image.setThumb(image);
|
|
|
- image.compressStyle = UMImage.CompressStyle.SCALE;
|
|
|
- new ShareAction(LiveRoomActivity.this).withMedia(image)
|
|
|
- .setPlatform(share_media)
|
|
|
- .setCallback(LiveRoomActivity.this)
|
|
|
- .share();
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
- ShareAction.addButton("群聊", "chat", "icon_share_chat_group", "icon_share_chat_group");
|
|
|
- ShareAction.open();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onDestroy() {
|
|
|
- if (presenter != null) {
|
|
|
-
|
|
|
- presenter.notifyLeaveRoomAction();
|
|
|
- }
|
|
|
- super.onDestroy();
|
|
|
- if (connectStatusListener != null) {
|
|
|
- IMCenter.getInstance().removeConnectionStatusListener(connectStatusListener);
|
|
|
- }
|
|
|
-
|
|
|
- if (hideHeaderInfoAnim != null) {
|
|
|
- hideHeaderInfoAnim.cancel();
|
|
|
- hideHeaderInfoAnim = null;
|
|
|
- }
|
|
|
-
|
|
|
- if (showHeaderInfoAnim != null) {
|
|
|
- showHeaderInfoAnim.cancel();
|
|
|
- showHeaderInfoAnim = null;
|
|
|
- }
|
|
|
- SoftKeyboardUtil.unregisterSoftInputChangedListener(getWindow());
|
|
|
- LiveRoomAnimatorHelper.getInstance().releaseAnimator();
|
|
|
- LiveRoomAddLikeHelper.getInstance().release();
|
|
|
- LiveEventHelper.getInstance().unRegister();
|
|
|
- RCRTCAudioRouteManager.getInstance().unInit();
|
|
|
- if (mHandler != null) {
|
|
|
- mHandler.removeCallbacksAndMessages(null);
|
|
|
- }
|
|
|
- if (mVideoViewManager != null) {
|
|
|
- mVideoViewManager.onRelease();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onSoftInputChanged(int height) {
|
|
|
- Log.i("pq", "height" + height);
|
|
|
- if (height == 0) {
|
|
|
-
|
|
|
- Log.i("pq", "SoftInput hide");
|
|
|
- if (mInputBarDialog != null && mInputBarDialog.isShowing()) {
|
|
|
- mInputBarDialog.dismiss();
|
|
|
- }
|
|
|
- } else {
|
|
|
-
|
|
|
- Log.i("pq", "SoftInput show");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onStart(SHARE_MEDIA share_media) {
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onResult(SHARE_MEDIA platform) {
|
|
|
-
|
|
|
- if (platform.name().equals("WEIXIN_FAVORITE")) {
|
|
|
- ToastUtil.getInstance().showShort("收藏成功啦");
|
|
|
- } else {
|
|
|
- if (platform != SHARE_MEDIA.MORE && platform != SHARE_MEDIA.SMS
|
|
|
- && platform != SHARE_MEDIA.EMAIL
|
|
|
- && platform != SHARE_MEDIA.FLICKR
|
|
|
- && platform != SHARE_MEDIA.FOURSQUARE
|
|
|
- && platform != SHARE_MEDIA.TUMBLR
|
|
|
- && platform != SHARE_MEDIA.POCKET
|
|
|
- && platform != SHARE_MEDIA.PINTEREST
|
|
|
-
|
|
|
- && platform != SHARE_MEDIA.INSTAGRAM
|
|
|
- && platform != SHARE_MEDIA.GOOGLEPLUS
|
|
|
- && platform != SHARE_MEDIA.YNOTE
|
|
|
- && platform != SHARE_MEDIA.EVERNOTE) {
|
|
|
- ToastUtil.getInstance().showShort("分享成功啦");
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onError(SHARE_MEDIA platform, Throwable throwable) {
|
|
|
- if (platform != SHARE_MEDIA.MORE && platform != SHARE_MEDIA.SMS
|
|
|
- && platform != SHARE_MEDIA.EMAIL
|
|
|
- && platform != SHARE_MEDIA.FLICKR
|
|
|
- && platform != SHARE_MEDIA.FOURSQUARE
|
|
|
- && platform != SHARE_MEDIA.TUMBLR
|
|
|
- && platform != SHARE_MEDIA.POCKET
|
|
|
- && platform != SHARE_MEDIA.PINTEREST
|
|
|
-
|
|
|
- && platform != SHARE_MEDIA.INSTAGRAM
|
|
|
- && platform != SHARE_MEDIA.GOOGLEPLUS
|
|
|
- && platform != SHARE_MEDIA.YNOTE
|
|
|
- && platform != SHARE_MEDIA.EVERNOTE) {
|
|
|
- ToastUtil.getInstance().showShort("分享失败啦");
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void onCancel(SHARE_MEDIA share_media) {
|
|
|
- ToastUtil.getInstance().showShort("分享取消了");
|
|
|
- }
|
|
|
-
|
|
|
- private boolean isShowShare() {
|
|
|
- if (TextUtils.equals(mRoomInfoBean.roomType, "TEMP")) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|