集成完直播后提交代码

This commit is contained in:
xuhuixiang
2026-02-06 14:55:21 +08:00
commit ea9ffa06ff
960 changed files with 75063 additions and 0 deletions

View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,55 @@
import java.text.SimpleDateFormat
plugins {
id 'com.android.library'
}
android {
compileSdkVersion androidCompileSdkVersion
buildToolsVersion androidBuildToolsVersion
defaultConfig {
minSdkVersion androidMinSdkVersion
targetSdkVersion androidTargetSdkVersion
versionCode 1
versionName "1.0"
consumerProguardFiles "consumer-rules.pro"
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME :'interactiveCommon' ]
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation externalAndroidDesign
api project(':LiveCommon:live_commonbiz')
api project(':LiveBeauty:live_queenbeauty')
if (project.hasProperty("sdk_type") && "AliVCSDK_InteractiveLive".equalsIgnoreCase(sdk_type)) {
api externalLivePusherInteractive
} else {
api externalLivePusher
}
implementation externalSimpleZXing
implementation externalARouter
annotationProcessor externalARouterCompiler
}

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.alivc.live.interactive_common">
<application>
<activity
android:name="com.alivc.live.interactive_common.InteractiveInputActivity"
android:alwaysRetainTaskState="true"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
<activity
android:name="com.alivc.live.interactive_common.InteractiveAppInfoActivity"
android:alwaysRetainTaskState="true"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
<activity
android:name=".InteractiveSettingActivity"
android:alwaysRetainTaskState="true"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
</application>
</manifest>

View File

@@ -0,0 +1,103 @@
package com.alivc.live.interactive_common;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.Fragment;
import com.alivc.live.commonbiz.SharedPreferenceUtils;
/**
* AppInfo 相关信息录入 Fragment
*/
public class InteractLiveAppInfoFragment extends Fragment {
private TextView mAppInfoTv;
private ImageView mArrowImageView;
private ConstraintLayout mAppInfoConstraintLayout;
private ImageView mEditImageView;
private TextView mAppIdTextView;
private TextView mAppKeyTextView;
private TextView mPlayDomainTextView;
private OnEditClickListener mOnEditClickListener;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_interact_live_appinfo, container, false);
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mAppInfoTv = view.findViewById(R.id.tv_app_info);
mArrowImageView = view.findViewById(R.id.iv_arrow);
mEditImageView = view.findViewById(R.id.iv_edit);
mAppInfoConstraintLayout = view.findViewById(R.id.cl_app_info);
mAppIdTextView = view.findViewById(R.id.tv_app_id);
mAppKeyTextView = view.findViewById(R.id.tv_app_key);
mPlayDomainTextView = view.findViewById(R.id.tv_play_domain);
mArrowImageView.setColorFilter(Color.WHITE);
initListener();
}
@Override
public void onResume() {
super.onResume();
mAppIdTextView.setText(SharedPreferenceUtils.getAppId(getContext().getApplicationContext()));
mAppKeyTextView.setText(SharedPreferenceUtils.getAppKey(getContext().getApplicationContext()));
mPlayDomainTextView.setText(SharedPreferenceUtils.getPlayDomain(getContext().getApplicationContext()));
}
private void initListener() {
// 测试用长按应用信息四个字自动填写用户ID和房间号
mAppInfoTv.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (mOnEditClickListener != null) {
mOnEditClickListener.onLongClickAppInfoForDebug();
}
return false;
}
});
mArrowImageView.setOnClickListener((view) -> {
float mArrowImageViewRotation;
if (mAppInfoConstraintLayout.isShown()) {
mAppInfoConstraintLayout.setVisibility(View.GONE);
mArrowImageViewRotation = 0;
} else {
mAppInfoConstraintLayout.setVisibility(View.VISIBLE);
mArrowImageViewRotation = 180;
}
mArrowImageView.setRotation(mArrowImageViewRotation);
});
mEditImageView.setOnClickListener((view) -> {
if (mOnEditClickListener != null) {
mOnEditClickListener.onEditClickListener();
}
});
}
public interface OnEditClickListener {
void onLongClickAppInfoForDebug();
void onEditClickListener();
}
public void setOnEditClickListener(OnEditClickListener listener) {
this.mOnEditClickListener = listener;
}
}

View File

@@ -0,0 +1,1211 @@
package com.alivc.live.interactive_common;
import static com.alivc.live.interactive_common.utils.LivePushGlobalConfig.mAlivcLivePushConfig;
import android.content.Context;
import android.graphics.Bitmap;
import android.hardware.Camera;
import android.text.TextUtils;
import android.util.Log;
import android.widget.FrameLayout;
import com.alivc.component.custom.AlivcLivePushCustomFilter;
import com.alivc.live.annotations.AlivcLiveAudioFrameObserverOperationMode;
import com.alivc.live.annotations.AlivcLiveAudioFrameObserverUserDefinedInfoBitMask;
import com.alivc.live.annotations.AlivcLiveAudioSource;
import com.alivc.live.annotations.AlivcLiveMode;
import com.alivc.live.annotations.AlivcLiveMuteLocalAudioMode;
import com.alivc.live.annotations.AlivcLiveNetworkQuality;
import com.alivc.live.annotations.AlivcLivePushKickedOutType;
import com.alivc.live.annotations.AlivcLiveRecordMediaEvent;
import com.alivc.live.beauty.BeautyFactory;
import com.alivc.live.beauty.BeautyInterface;
import com.alivc.live.beauty.constant.BeautySDKType;
import com.alivc.live.commonbiz.backdoor.BackDoorInstance;
import com.alivc.live.commonbiz.seidelay.SEIDelayManager;
import com.alivc.live.commonbiz.seidelay.SEISourceType;
import com.alivc.live.commonbiz.seidelay.api.ISEIDelayEventListener;
import com.alivc.live.commonbiz.test.AliLiveStreamURLUtil;
import com.alivc.live.commonutils.ToastUtils;
import com.alivc.live.interactive_common.bean.InteractiveLivePlayer;
import com.alivc.live.interactive_common.bean.InteractiveUserData;
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
import com.alivc.live.interactive_common.manager.TimestampWatermarkManager;
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
import com.alivc.live.player.AlivcLivePlayConfig;
import com.alivc.live.player.AlivcLivePlayer;
import com.alivc.live.player.annotations.AlivcLivePlayError;
import com.alivc.live.player.annotations.AlivcLivePlayVideoStreamType;
import com.alivc.live.pusher.AlivcAudioChannelEnum;
import com.alivc.live.pusher.AlivcAudioSampleRateEnum;
import com.alivc.live.pusher.AlivcImageFormat;
import com.alivc.live.pusher.AlivcLiveAudioEffectConfig;
import com.alivc.live.pusher.AlivcLiveAudioFrameObserverConfig;
import com.alivc.live.pusher.AlivcLiveBase;
import com.alivc.live.pusher.AlivcLiveLocalRecordConfig;
import com.alivc.live.pusher.AlivcLiveMixStream;
import com.alivc.live.pusher.AlivcLivePublishState;
import com.alivc.live.pusher.AlivcLivePushAudioEffectVoiceChangeMode;
import com.alivc.live.pusher.AlivcLivePushAudioFrame;
import com.alivc.live.pusher.AlivcLivePushAudioFrameListener;
import com.alivc.live.pusher.AlivcLivePushError;
import com.alivc.live.pusher.AlivcLivePushErrorListener;
import com.alivc.live.pusher.AlivcLivePushExternalAudioStreamConfig;
import com.alivc.live.pusher.AlivcLivePushInfoListener;
import com.alivc.live.pusher.AlivcLivePushNetworkListener;
import com.alivc.live.pusher.AlivcLivePushStatsInfo;
import com.alivc.live.pusher.AlivcLivePushVideoConfig;
import com.alivc.live.pusher.AlivcLivePusher;
import com.alivc.live.pusher.AlivcLivePusherRawDataSample;
import com.alivc.live.pusher.AlivcLiveTranscodingConfig;
import com.alivc.live.pusher.AlivcPreviewDisplayMode;
import com.alivc.live.pusher.AlivcResolutionEnum;
import com.alivc.live.pusher.AlivcSoundFormat;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* 直播连麦基础类
*
* @note 请参考 API 文档,了解对应接口的释义,辅助接入
* @note 互动模式下,请使用`AlivcRTC`作为日志tag进行自主排障。
*/
public class InteractLiveBaseManager {
static {
// 1. 注册推流SDK
AlivcLiveBase.registerSDK();
}
protected static final String TAG = "InteractLiveBaseManager";
protected InteractiveMode mInteractiveMode;
// 这个TM用来做混流的
protected FrameLayout mAudienceFrameLayout;
//多人连麦混流
protected final ArrayList<AlivcLiveMixStream> mMultiInteractLiveMixStreamsArray = new ArrayList<>();
//多人连麦 Config
protected final AlivcLiveTranscodingConfig mMixInteractLiveTranscodingConfig = new AlivcLiveTranscodingConfig();
protected Context mContext;
protected AlivcLivePusher mAlivcLivePusher;
private InteractiveUserData mPushUserData;
protected final Map<String, InteractiveLivePlayer> mInteractiveLivePlayerMap = new HashMap<>();
protected final Map<String, InteractiveUserData> mInteractiveUserDataMap = new HashMap<>();
protected InteractLivePushPullListener mInteractLivePushPullListener;
// 美颜处理类需对接Queen美颜SDK且拥有License拥有美颜能力
private BeautyInterface mBeautyManager;
// CameraId美颜需要使用
private int mCameraId;
// 互动模式下的水印处理类
private TimestampWatermarkManager mTimestampWatermarkManager;
protected final SEIDelayManager mSEIDelayManager = new SEIDelayManager();
public void init(Context context, InteractiveMode interactiveMode) {
mInteractiveMode = interactiveMode;
mContext = context.getApplicationContext();
initLivePusher(interactiveMode);
// initWatermarks();
mSEIDelayManager.registerReceiver(new ISEIDelayEventListener() {
@Override
public void onEvent(String src, String type, String msg) {
if (mInteractLivePushPullListener != null) {
mInteractLivePushPullListener.onReceiveSEIDelay(src, type, msg);
}
}
});
}
/**
* 2. 初始化 AlivcLivePushConfig 和 AlivcLivePusher
*
* @param interactiveMode 直播连麦模式直播连麦、RTS 2.0推拉裸流)
*/
private void initLivePusher(InteractiveMode interactiveMode) {
// 初始化推流配置类
// 设置推流模式为 AlivcLiveInteractiveMode互动模式互动直播支持连麦、PK等实时互动
mAlivcLivePushConfig.setLivePushMode(AlivcLiveMode.AlivcLiveInteractiveMode);
// 互动模式下如需与Web连麦互通必须使用H5兼容模式否则Web用户查看Native用户将是黑屏。
mAlivcLivePushConfig.setH5CompatibleMode(LivePushGlobalConfig.IS_H5_COMPATIBLE);
// 互动模式下开启RTS推拉裸流(直推&直拉,不同于直播连麦)
if (InteractiveMode.isBareStream(interactiveMode)) {
mAlivcLivePushConfig.setEnableRTSForInteractiveMode(true);
}
/// 如果是外部音视频推流,需要初始化的 Pusher 配置以及自定义音频、视频的Profiles
if (mAlivcLivePushConfig.isExternMainStream()) {
mAlivcLivePushConfig.setExternMainStream(true, AlivcImageFormat.IMAGE_FORMAT_YUVNV12, AlivcSoundFormat.SOUND_FORMAT_S16);
mAlivcLivePushConfig.setAudioChannels(AlivcAudioChannelEnum.AUDIO_CHANNEL_ONE);
mAlivcLivePushConfig.setAudioSampleRate(AlivcAudioSampleRateEnum.AUDIO_SAMPLE_RATE_44100);
}
// 如果需要开启 Data Channel 自定义消息通道,以代替 SEI 功能,需要预先开启 Data Channel 自定义消息通道
if (LivePushGlobalConfig.IS_DATA_CHANNEL_MESSAGE_ENABLE) {
mAlivcLivePushConfig.setEnableDataChannelMessage(true);
}
// 开启强制耳返
HashMap<String, String> earbackExtras = new HashMap<>(4);
earbackExtras.put("earback_open_when_without_headset", LivePushGlobalConfig.IS_EARBACK_OPEN_WITHOUT_HEADSET ? "TRUE" : "FALSE");
mAlivcLivePushConfig.setExtras(earbackExtras);
// Demo逻辑用于测试请勿follow如果强制RTC预发环境那么增加extra配置
if (BackDoorInstance.getInstance().isForceRTCPreEnvironment()) {
HashMap<String, String> extras = new HashMap<>(4);
extras.put("user_specified_environment", "PRE_RELEASE");
mAlivcLivePushConfig.setExtras(extras);
}
// 初始化推流引擎
mAlivcLivePusher = new AlivcLivePusher();
mAlivcLivePusher.init(mContext.getApplicationContext(), mAlivcLivePushConfig);
// 设置音量回调频率和平滑系数
mAlivcLivePusher.enableAudioVolumeIndication(300, 3, 1);
/*
* 错误异常及特殊场景处理
* 参考文档https://help.aliyun.com/zh/live/developer-reference/handling-of-exceptions-and-special-scenarios-for-android
*/
// 设置推流错误事件
mAlivcLivePusher.setLivePushErrorListener(new AlivcLivePushErrorListener() {
@Override
public void onSystemError(AlivcLivePusher alivcLivePusher, AlivcLivePushError alivcLivePushError) {
Log.e(TAG, "onSystemError: " + alivcLivePushError);
if (mInteractLivePushPullListener != null) {
mInteractLivePushPullListener.onPushError();
}
}
@Override
public void onSDKError(AlivcLivePusher alivcLivePusher, AlivcLivePushError alivcLivePushError) {
Log.e(TAG, "onSDKError: " + alivcLivePushError);
if (mInteractLivePushPullListener != null) {
mInteractLivePushPullListener.onPushError();
}
}
});
// 设置推流通知事件
mAlivcLivePusher.setLivePushInfoListener(new AlivcLivePushInfoListener() {
@Override
public void onPreviewStarted(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onPreviewStarted: ");
if (mInteractLivePushPullListener != null) {
mInteractLivePushPullListener.onPushSuccess();
}
}
@Override
public void onPreviewStopped(AlivcLivePusher pusher) {
Log.d(TAG, "onPreviewStopped: ");
}
@Override
public void onPushStarted(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onPushStarted: ");
if (mInteractLivePushPullListener != null) {
mInteractLivePushPullListener.onPushSuccess();
}
}
@Override
public void onPushPaused(AlivcLivePusher pusher) {
Log.d(TAG, "onPushPaused: ");
}
@Override
public void onPushResumed(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onPushResumed: ");
}
@Override
public void onPushStopped(AlivcLivePusher pusher) {
Log.d(TAG, "onPushStopped: ");
pusher.stopPreview();
}
@Override
public void onPushRestarted(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onPushRestarted: ");
}
@Override
public void onFirstFramePushed(AlivcLivePusher pusher) {
Log.d(TAG, "onFirstFramePushed: ");
}
@Override
public void onFirstFramePreviewed(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onFirstFramePreviewed: ");
}
@Override
public void onDropFrame(AlivcLivePusher alivcLivePusher, int i, int i1) {
Log.d(TAG, "onDropFrame: ");
}
@Override
public void onAdjustBitrate(AlivcLivePusher pusher, int currentBitrate, int targetBitrate) {
Log.i(TAG, "onAdjustBitrate: " + currentBitrate + "->" + targetBitrate);
}
@Override
public void onAdjustFps(AlivcLivePusher alivcLivePusher, int i, int i1) {
Log.d(TAG, "onAdjustFps: ");
}
@Override
public void onPushStatistics(AlivcLivePusher alivcLivePusher, AlivcLivePushStatsInfo alivcLivePushStatsInfo) {
// Log.i(TAG, "onPushStatistics: " + alivcLivePushStatsInfo);
}
@Override
public void onSetLiveMixTranscodingConfig(AlivcLivePusher alivcLivePusher, boolean b, String s) {
Log.d(TAG, "onSetLiveMixTranscodingConfig: ");
}
@Override
public void onKickedOutByServer(AlivcLivePusher pusher, AlivcLivePushKickedOutType kickedOutType) {
Log.d(TAG, "onKickedOutByServer: " + kickedOutType);
if (mInteractLivePushPullListener != null) {
mInteractLivePushPullListener.onPushError();
}
}
@Override
public void onMicrophoneVolumeUpdate(AlivcLivePusher pusher, int volume) {
// 麦克风音量回调仅互动模式下生效需设置AlivcLivePusher#enableAudioVolumeIndication接口
// Log.d(TAG, "onMicrophoneVolumeUpdate: " + volume);
}
@Override
public void onLocalRecordEvent(AlivcLiveRecordMediaEvent mediaEvent, String storagePath) {
ToastUtils.show(mediaEvent + ", " + storagePath);
}
@Override
public void onRemoteUserEnterRoom(AlivcLivePusher pusher, String userId, boolean isOnline) {
ToastUtils.show("onRemoteUserEnterRoom: " + userId + ", isOnline" + isOnline);
}
@Override
public void onRemoteUserAudioStream(AlivcLivePusher pusher, String userId, boolean isPushing) {
ToastUtils.show("onRemoteUserAudioStream: " + userId + ", isPushing: " + isPushing);
}
@Override
public void onRemoteUserVideoStream(AlivcLivePusher pusher, String userId, AlivcLivePlayVideoStreamType videoStreamType, boolean isPushing) {
ToastUtils.show("onRemoteUserVideoStream: " + userId + ", videoStreamType: " + videoStreamType + ", isPushing: " + isPushing);
}
@Override
public void onAudioPublishStateChanged(AlivcLivePublishState oldState, AlivcLivePublishState newState) {
super.onAudioPublishStateChanged(oldState, newState);
ToastUtils.show("onAudioPublishStateChanged: " + oldState + "->" + newState);
}
@Override
public void onVideoPublishStateChanged(AlivcLivePublishState oldState, AlivcLivePublishState newState) {
super.onVideoPublishStateChanged(oldState, newState);
ToastUtils.show("onVideoPublishStateChanged: " + oldState + "->" + newState);
}
@Override
public void onScreenSharePublishStateChanged(AlivcLivePublishState oldState, AlivcLivePublishState newState) {
super.onScreenSharePublishStateChanged(oldState, newState);
ToastUtils.show("onScreenSharePublishStateChanged: " + oldState + "->" + newState);
}
@Override
public void onLocalDualAudioStreamPushState(AlivcLivePusher pusher, boolean isPushing) {
super.onLocalDualAudioStreamPushState(pusher, isPushing);
ToastUtils.show("onLocalDualAudioStreamPushState: " + isPushing);
}
});
// 设置网络通知事件
mAlivcLivePusher.setLivePushNetworkListener(new AlivcLivePushNetworkListener() {
@Override
public void onNetworkPoor(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onNetworkPoor: ");
}
@Override
public void onNetworkRecovery(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onNetworkRecovery: ");
}
@Override
public void onReconnectStart(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onReconnectStart: ");
}
@Override
public void onConnectionLost(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onConnectionLost: ");
if (mInteractLivePushPullListener != null) {
mInteractLivePushPullListener.onConnectionLost();
}
}
@Override
public void onReconnectFail(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onReconnectFail: ");
}
@Override
public void onReconnectSucceed(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onReconnectSucceed: ");
}
@Override
public void onSendDataTimeout(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onSendDataTimeout: ");
}
@Override
public void onConnectFail(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onConnectFail: ");
}
@Override
public void onNetworkQualityChanged(AlivcLiveNetworkQuality upQuality, AlivcLiveNetworkQuality downQuality) {
}
@Override
public String onPushURLAuthenticationOverdue(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onPushURLAuthenticationOverdue: ");
return null;
}
@Override
public void onPushURLTokenWillExpire(AlivcLivePusher pusher) {
Log.d(TAG, "onPushURLTokenWillExpire: ");
}
@Override
public void onPushURLTokenExpired(AlivcLivePusher pusher) {
Log.d(TAG, "onPushURLTokenExpired: ");
}
@Override
public void onSendMessage(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onSendMessage: ");
}
@Override
public void onPacketsLost(AlivcLivePusher alivcLivePusher) {
Log.d(TAG, "onPacketsLost: ");
}
});
// 【美颜】注册视频前处理回调
mAlivcLivePusher.setCustomFilter(new AlivcLivePushCustomFilter() {
@Override
public void customFilterCreate() {
initBeautyManager();
}
@Override
public int customFilterProcess(int inputTexture, int textureWidth, int textureHeight, long extra) {
if (mBeautyManager == null) {
return inputTexture;
}
return mBeautyManager.onTextureInput(inputTexture, textureWidth, textureHeight);
}
@Override
public void customFilterDestroy() {
destroyBeautyManager();
}
});
// 记录当前摄像头状态
mCameraId = mAlivcLivePushConfig.getCameraType();
}
private void destroyLivePusher() {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.destroy();
}
}
public boolean isPushing() {
return mAlivcLivePusher != null && mAlivcLivePusher.isPushing();
}
public boolean isPulling(InteractiveUserData userData) {
if (userData == null) {
return false;
}
InteractiveLivePlayer interactiveLivePlayer = mInteractiveLivePlayerMap.get(userData.getKey());
if (interactiveLivePlayer != null) {
return interactiveLivePlayer.isPulling();
}
return false;
}
private void initWatermarks() {
// 互动模式下使用该接口添加水印,最多添加一个,多则替代
mTimestampWatermarkManager = new TimestampWatermarkManager();
mTimestampWatermarkManager.init(new TimestampWatermarkManager.OnWatermarkListener() {
@Override
public void onWatermarkUpdate(Bitmap bitmap) {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.addWaterMark(bitmap, 0, 0, 1);
}
}
});
}
private void destroyWatermarks() {
if (mTimestampWatermarkManager != null) {
mTimestampWatermarkManager.destroy();
mTimestampWatermarkManager = null;
}
}
public void setInteractLivePushPullListener(InteractLivePushPullListener listener) {
mInteractLivePushPullListener = listener;
}
private boolean createAlivcLivePlayer(InteractiveUserData userData) {
if (userData == null) {
return false;
}
String userDataKey = userData.getKey();
if (mInteractiveLivePlayerMap.containsKey(userDataKey)) {
Log.i(TAG, "createAlivcLivePlayer already created " + userDataKey);
return false;
}
Log.d(TAG, "createAlivcLivePlayer: " + userDataKey);
InteractiveLivePlayer alivcLivePlayer = new InteractiveLivePlayer(mContext, AlivcLiveMode.AlivcLiveInteractiveMode);
alivcLivePlayer.setPullUserData(userData);
alivcLivePlayer.setMultiInteractPlayInfoListener(new InteractLivePushPullListener() {
@Override
public void onPullSuccess(InteractiveUserData userData) {
if (mInteractLivePushPullListener != null) {
mInteractLivePushPullListener.onPullSuccess(userData);
}
}
@Override
public void onPullError(InteractiveUserData userData, AlivcLivePlayError errorType, String errorMsg) {
if (mInteractLivePushPullListener != null) {
mInteractLivePushPullListener.onPullError(userData, errorType, errorMsg);
}
}
@Override
public void onPullStop(InteractiveUserData userData) {
if (mInteractLivePushPullListener != null) {
mInteractLivePushPullListener.onPullStop(userData);
}
}
@Override
public void onPushSuccess() {
if (mInteractLivePushPullListener != null) {
mInteractLivePushPullListener.onPushSuccess();
}
}
@Override
public void onPushError() {
if (mInteractLivePushPullListener != null) {
mInteractLivePushPullListener.onPushError();
}
}
@Override
public void onReceiveSEIMessage(int payload, byte[] data) {
super.onReceiveSEIMessage(payload, data);
if (mInteractLivePushPullListener != null) {
mInteractLivePushPullListener.onReceiveSEIMessage(payload, data);
}
String sei = new String(data, StandardCharsets.UTF_8);
mSEIDelayManager.receiveSEI(SEISourceType.RTC, sei);
}
@Override
public void onVideoEnabled(boolean enable) {
super.onVideoEnabled(enable);
if (mInteractLivePushPullListener != null) {
mInteractLivePushPullListener.onVideoEnabled(enable);
}
}
});
mInteractiveLivePlayerMap.put(userDataKey, alivcLivePlayer);
mInteractiveUserDataMap.put(userDataKey, userData);
return true;
}
public void startPreviewAndPush(InteractiveUserData userData, FrameLayout frameLayout, boolean isAnchor) {
if (userData == null) {
Log.e(TAG, "startPreviewAndPush, userData invalid!");
return;
}
mPushUserData = userData;
mAlivcLivePusher.startPreview(mContext, frameLayout, isAnchor);
String pushUrl = userData.url;
if (TextUtils.isEmpty(pushUrl) && InteractiveMode.isInteractive(mInteractiveMode)) {
pushUrl = AliLiveStreamURLUtil.generateInteractivePushUrl(userData.channelId, userData.userId);
}
if (TextUtils.isEmpty(pushUrl)) {
Log.e(TAG, "startPreviewAndPush, pushUrl invalid!");
return;
}
Log.d(TAG, "startPreviewAndPush: " + isAnchor + ", " + pushUrl);
mAlivcLivePusher.startPushAsync(pushUrl);
mSEIDelayManager.registerProvider(userData.userId, new ISEIDelayEventListener() {
@Override
public void onEvent(String src, String type, String msg) {
sendCustomMessage(msg, 5);
}
});
}
public void stopPreview() {
mAlivcLivePusher.stopPreview();
}
public void updatePreview(FrameLayout frameLayout, boolean isFullScreen) {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.updatePreview(mContext, frameLayout, isFullScreen);
}
}
public void stopPush() {
if (isPushing()) {
mAlivcLivePusher.stopPush();
}
}
public void stopCamera() {
mAlivcLivePusher.stopCamera();
}
public void setPullView(InteractiveUserData userData, FrameLayout frameLayout, boolean isAnchor) {
if (userData == null) {
return;
}
this.mAudienceFrameLayout = frameLayout;
boolean isNew = createAlivcLivePlayer(userData);
AlivcLivePlayer livePlayer = mInteractiveLivePlayerMap.get(userData.getKey());
if (livePlayer == null) {
Log.e(TAG, "setPullView error: livePlayer is empty");
return;
}
if (isNew) {
AlivcLivePlayConfig config = new AlivcLivePlayConfig();
config.isFullScreen = isAnchor;
config.videoStreamType = userData.videoStreamType;
config.audioStreamType = userData.audioStreamType;
livePlayer.setupWithConfig(config);
}
livePlayer.setPlayView(frameLayout);
Log.i(TAG, "setPullView: " + userData);
}
public void startPullRTCStream(InteractiveUserData userData) {
if (userData == null || TextUtils.isEmpty(userData.url)) {
Log.e(TAG, "startPull error: url is empty");
return;
}
createAlivcLivePlayer(userData);
AlivcLivePlayer alivcLivePlayer = mInteractiveLivePlayerMap.get(userData.getKey());
Log.d(TAG, "startPullRTCStream: " + userData.getKey() + ", " + userData.url + ", " + alivcLivePlayer);
if (alivcLivePlayer != null) {
alivcLivePlayer.startPlay(userData.url);
}
}
public void stopPullRTCStream(InteractiveUserData userData) {
if (userData == null) {
return;
}
String userKey = userData.getKey();
AlivcLivePlayer alivcLivePlayer = mInteractiveLivePlayerMap.get(userKey);
Log.d(TAG, "stopPullRTCStream: " + userData.getKey() + ", " + userData.url + ", " + alivcLivePlayer);
if (alivcLivePlayer != null) {
alivcLivePlayer.stopPlay();
alivcLivePlayer.destroy();
}
mInteractiveLivePlayerMap.remove(userKey);
mInteractiveUserDataMap.remove(userKey);
}
// 暂停推流
public void pausePush() {
if (isPushing()) {
mAlivcLivePusher.pause();
}
}
// 恢复推流
public void resumePush() {
if (isPushing()) {
mAlivcLivePusher.resume();
}
}
// 停止播放RTC流
public void pausePlayRTCStream(InteractiveUserData userData) {
Log.d(TAG, "pausePlayRTCStream: " + userData);
if (userData == null) {
return;
}
String userKey = userData.getKey();
InteractiveLivePlayer alivcLivePlayer = mInteractiveLivePlayerMap.get(userKey);
if (alivcLivePlayer == null) {
return;
}
alivcLivePlayer.pauseVideoPlaying();
}
// 恢复播放RTC流
public void resumePlayRTCStream(InteractiveUserData userData) {
Log.d(TAG, "resumePlayRTCStream: " + userData);
if (userData == null) {
return;
}
String userKey = userData.getKey();
InteractiveLivePlayer alivcLivePlayer = mInteractiveLivePlayerMap.get(userKey);
if (alivcLivePlayer == null) {
return;
}
alivcLivePlayer.resumeVideoPlaying();
}
// 停止播放所有RTC流
public void pausePlayRTCStream() {
for (InteractiveUserData userData : mInteractiveUserDataMap.values()) {
pausePlayRTCStream(userData);
}
}
// 恢复播放所有RTC流
public void resumePlayRTCStream() {
for (InteractiveUserData userData : mInteractiveUserDataMap.values()) {
resumePlayRTCStream(userData);
}
}
// 发送自定义消息发送SEI
public void sendCustomMessage(String text, int payload) {
if (LivePushGlobalConfig.IS_DATA_CHANNEL_MESSAGE_ENABLE) {
mAlivcLivePusher.sendDataChannelMessage(text);
} else {
mAlivcLivePusher.sendMessage(text, 0, 0, false, payload);
}
}
// 开始本地录制
public void startLocalRecord(AlivcLiveLocalRecordConfig recordConfig) {
boolean flag = false;
if (mAlivcLivePusher != null) {
flag = mAlivcLivePusher.startLocalRecord(recordConfig);
}
if (!flag) {
ToastUtils.show("start local record failed!");
}
}
// 结束本地录制
public void stopLocalRecord() {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.stopLocalRecord();
}
}
public void switchCamera() {
if (isPushing()) {
mAlivcLivePusher.switchCamera();
if (mCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
mCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
} else {
mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
}
}
}
public void setMute(boolean isMute) {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.setMute(isMute);
}
}
public void setMute(boolean isMute, AlivcLiveMuteLocalAudioMode muteLocalAudioMode) {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.setMute(isMute, muteLocalAudioMode);
}
}
public void setBGMEarsBack(boolean isOpen) {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.setBGMEarsBack(isOpen);
}
}
public void setAudioEffectVoiceChangeMode(AlivcLivePushAudioEffectVoiceChangeMode voiceChangeMode) {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.setAudioEffectVoiceChangeMode(voiceChangeMode);
}
}
public void setVideoConfig(AlivcLivePushVideoConfig videoConfig) {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.setVideoConfig(videoConfig);
}
}
public void enableSpeakerPhone(boolean enableSpeakerPhone) {
mAlivcLivePusher.enableSpeakerphone(enableSpeakerPhone);
}
public void enableAudioCapture(boolean enable) {
if (enable) {
mAlivcLivePusher.startAudioCapture(true);
} else {
mAlivcLivePusher.stopAudioCapture();
}
}
public void muteLocalCamera(boolean muteLocalCamera) {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.muteLocalCamera(muteLocalCamera);
}
}
public void enableLocalCamera(boolean enable) {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.enableLocalCamera(enable);
}
}
public void changeResolution(AlivcResolutionEnum resolutionEnum) {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.changeResolution(resolutionEnum);
}
}
public void release() {
mSEIDelayManager.destroy();
// destroyWatermarks();
clearLiveMixTranscodingConfig();
stopPush();
destroyLivePusher();
mMultiInteractLiveMixStreamsArray.clear();
mMixInteractLiveTranscodingConfig.mixStreams = mMultiInteractLiveMixStreamsArray;
for (AlivcLivePlayer alivcLivePlayer : mInteractiveLivePlayerMap.values()) {
alivcLivePlayer.stopPlay();
alivcLivePlayer.destroy();
}
mInteractiveLivePlayerMap.clear();
mInteractiveUserDataMap.clear();
mInteractLivePushPullListener = null;
}
// 单切混的逻辑在各自场景的manager里面因为PK和连麦的混流布局不太一样。
// 更新混流、混切单
public void clearLiveMixTranscodingConfig() {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.setLiveMixTranscodingConfig(null);
}
}
// 遍历当前混流子布局信息
public AlivcLiveMixStream findMixStreamByUserData(InteractiveUserData userData) {
if (userData == null || TextUtils.isEmpty(userData.userId)) {
return null;
}
for (AlivcLiveMixStream alivcLiveMixStream : mMultiInteractLiveMixStreamsArray) {
if (userData.userId.equals(alivcLiveMixStream.userId)
&& alivcLiveMixStream.mixSourceType == InteractiveBaseUtil.covertVideoStreamType2MixSourceType(userData.videoStreamType)) {
return alivcLiveMixStream;
}
}
return null;
}
public InteractiveUserData getUserDataByKey(String key) {
if (TextUtils.isEmpty(key)) {
return null;
}
for (String userKey : mInteractiveUserDataMap.keySet()) {
if (TextUtils.equals(userKey, key)) {
return mInteractiveUserDataMap.get(userKey);
}
}
return null;
}
// 初始化美颜相关资源
private void initBeautyManager() {
if (mBeautyManager == null && LivePushGlobalConfig.ENABLE_BEAUTY) {
// v4.4.4版本-v6.1.0版本互动模式下的美颜处理逻辑参考BeautySDKType.INTERACT_QUEENInteractQueenBeautyImpl
// v6.1.0以后的版本从v6.2.0开始基础模式下的美颜和互动模式下的美颜处理逻辑保持一致QueenBeautyImpl
mBeautyManager = BeautyFactory.createBeauty(BeautySDKType.QUEEN, mContext);
// initialize in texture thread.
mBeautyManager.init();
mBeautyManager.setBeautyEnable(LivePushGlobalConfig.ENABLE_BEAUTY);
mBeautyManager.switchCameraId(mCameraId);
}
}
// 销毁美颜相关资源
private void destroyBeautyManager() {
if (mBeautyManager != null) {
mBeautyManager.release();
mBeautyManager = null;
}
}
/**
* ****************** 音频数据回调(参考示例) ****************** [START]
* <p>
* 调用建议1. 调用时机任意2. 注意清除逻辑
*/
private static final AlivcLiveAudioSource AUDIO_FRAME_OBSERVER_SOURCE = AlivcLiveAudioSource.PROCESS_CAPTURED;
private static final AlivcLiveAudioFrameObserverOperationMode AUDIO_FRAME_OBSERVER_MODE = AlivcLiveAudioFrameObserverOperationMode.READ_WRITE;
private static final AlivcLiveAudioFrameObserverUserDefinedInfoBitMask AUDIO_FRAME_OBSERVER_BIT_MASK = AlivcLiveAudioFrameObserverUserDefinedInfoBitMask.MIX_EXTERNAL_CAPTURE;
/**
* 设置音频裸数据回调
* <p>
* 注意:该接口较耗时,会影响性能,如果不需要对音频裸数据进行前处理,建议关闭
*/
public void enableAudioFrameObserver(boolean enable) {
if (mAlivcLivePusher == null) {
return;
}
if (enable) {
AlivcLiveAudioFrameObserverConfig audioFrameObserverConfig = new AlivcLiveAudioFrameObserverConfig();
audioFrameObserverConfig.mode = AUDIO_FRAME_OBSERVER_MODE;
audioFrameObserverConfig.userDefinedInfo = AUDIO_FRAME_OBSERVER_BIT_MASK;
mAlivcLivePusher.enableAudioFrameObserver(true, AUDIO_FRAME_OBSERVER_SOURCE, audioFrameObserverConfig);
mAlivcLivePusher.setLivePushAudioFrameListener(new AlivcLivePushAudioFrameListener() {
@Override
public boolean onCapturedAudioFrame(AlivcLivePushAudioFrame audioFrame) {
// 设置 enableAudioFrameObserver 接口中的 AlivcLiveAudioSource 为 CAPTURED会执行该回调
// do audio process here, and then, return true value if audio data need to be rewrited
return false;
}
@Override
public boolean onProcessCapturedAudioFrame(AlivcLivePushAudioFrame audioFrame) {
// 设置 enableAudioFrameObserver 接口中的 AlivcLiveAudioSource 为 PROCESS_CAPTURED会执行该回调
// do audio process here, and then, return true value if audio data need to be rewrited
return false;
// // 清空 byte buffer测试是否生效
// Arrays.fill(audioFrame.data, (byte) 0);
// return true;
}
@Override
public boolean onPublishAudioFrame(AlivcLivePushAudioFrame audioFrame) {
// 设置 enableAudioFrameObserver 接口中的 AlivcLiveAudioSource 为 PUB会执行该回调
// do audio process here, and then, return true value if audio data need to be rewrited
return false;
}
@Override
public boolean onPlaybackAudioFrame(AlivcLivePushAudioFrame audioFrame) {
// 设置 enableAudioFrameObserver 接口中的 AlivcLiveAudioSource 为 PLAYBACK会执行该回调
// do audio process here, and then, return true value if audio data need to be rewrited
return false;
}
@Override
public boolean onMixedAllAudioFrame(AlivcLivePushAudioFrame audioFrame) {
// 设置 enableAudioFrameObserver 接口中的 AlivcLiveAudioSource 为 MIXED_ALL会执行该回调
// 注意:设置 AlivcLiveAudioSource.MIXED_ALL 时,只能读不能写
// do audio process here, and then, return true value if audio data need to be rewrited
return false;
}
});
} else {
// please ensure the callback unregistration to prevent memory leaks
mAlivcLivePusher.enableAudioFrameObserver(false, AUDIO_FRAME_OBSERVER_SOURCE, null);
mAlivcLivePusher.setLivePushAudioFrameListener(null);
}
}
/**
* [END] ****************** 音频数据回调(参考示例) ******************
*/
/**
* ****************** 音效接口(参考示例) ****************** [START]
*/
// 音效文件 assets 路径
private static final String SOUND_EFFECT_ASSETS_PATH = "alivc_resource/sound_effect/";
// 预加载音效
public void loadAudioEffects() {
if (mAlivcLivePusher == null || mContext == null) {
return;
}
String soundEffectPath1 = mContext.getFilesDir().getPath() + File.separator + SOUND_EFFECT_ASSETS_PATH + "sound_effect_man_smile.mp3";
String soundEffectPath2 = mContext.getFilesDir().getPath() + File.separator + SOUND_EFFECT_ASSETS_PATH + "sound_effect_roar.mp3";
String soundEffectPath3 = mContext.getFilesDir().getPath() + File.separator + SOUND_EFFECT_ASSETS_PATH + "sound_effect_sinister_smile.mp3";
mAlivcLivePusher.preloadAudioEffect(1, soundEffectPath1);
mAlivcLivePusher.preloadAudioEffect(2, soundEffectPath2);
mAlivcLivePusher.preloadAudioEffect(3, soundEffectPath3);
}
// 取消预加载音效
public void unloadAudioEffects() {
if (mAlivcLivePusher == null || mContext == null) {
return;
}
mAlivcLivePusher.unloadAudioEffect(1);
mAlivcLivePusher.unloadAudioEffect(2);
mAlivcLivePusher.unloadAudioEffect(3);
}
// 播放音效
public void playAudioEffects() {
if (mAlivcLivePusher == null || mContext == null) {
return;
}
String soundEffectPath1 = mContext.getFilesDir().getPath() + File.separator + SOUND_EFFECT_ASSETS_PATH + "sound_effect_man_smile.mp3";
String soundEffectPath2 = mContext.getFilesDir().getPath() + File.separator + SOUND_EFFECT_ASSETS_PATH + "sound_effect_roar.mp3";
String soundEffectPath3 = mContext.getFilesDir().getPath() + File.separator + SOUND_EFFECT_ASSETS_PATH + "sound_effect_sinister_smile.mp3";
AlivcLiveAudioEffectConfig audioEffectConfig1 = new AlivcLiveAudioEffectConfig();
audioEffectConfig1.needPublish = false;
audioEffectConfig1.publishVolume = 0;
audioEffectConfig1.playoutVolume = 0;
AlivcLiveAudioEffectConfig audioEffectConfig2 = new AlivcLiveAudioEffectConfig();
audioEffectConfig2.needPublish = true;
audioEffectConfig2.publishVolume = 100;
audioEffectConfig2.playoutVolume = 100;
AlivcLiveAudioEffectConfig audioEffectConfig3 = new AlivcLiveAudioEffectConfig();
audioEffectConfig3.needPublish = true;
audioEffectConfig3.publishVolume = 100;
audioEffectConfig3.playoutVolume = 100;
audioEffectConfig3.loopCycles = 3;
mAlivcLivePusher.playAudioEffect(1, soundEffectPath1, audioEffectConfig1);
mAlivcLivePusher.playAudioEffect(2, soundEffectPath2, audioEffectConfig2);
mAlivcLivePusher.playAudioEffect(3, soundEffectPath3, audioEffectConfig3);
}
public void stopAudioEffect() {
if (mAlivcLivePusher == null) {
return;
}
mAlivcLivePusher.stopAudioEffect(1);
mAlivcLivePusher.stopAudioEffect(2);
mAlivcLivePusher.stopAudioEffect(3);
// mAlivcLivePusher.stopAllAudioEffects();
}
public void pauseAudioEffect() {
if (mAlivcLivePusher == null) {
return;
}
mAlivcLivePusher.pauseAudioEffect(1);
mAlivcLivePusher.pauseAudioEffect(2);
mAlivcLivePusher.pauseAudioEffect(3);
// mAlivcLivePusher.pauseAllAudioEffects();
}
public void resumeAudioEffect() {
if (mAlivcLivePusher == null) {
return;
}
mAlivcLivePusher.resumeAudioEffect(1);
mAlivcLivePusher.resumeAudioEffect(2);
mAlivcLivePusher.resumeAudioEffect(3);
// mAlivcLivePusher.resumeAllAudioEffects();
}
/**
* [END] ****************** 音效接口(参考示例) ******************
*/
/**
* ****************** 外部音视频输入/三方推流(参考示例) ****************** [START]
*/
// 新增外部音频流
public int addExternalAudioStream(AlivcLivePushExternalAudioStreamConfig externalAudioStreamConfig) {
if (mAlivcLivePusher != null) {
return mAlivcLivePusher.addExternalAudioStream(externalAudioStreamConfig);
}
return -1;
}
// 删除外部音频流
public int removeExternalAudioStream(int streamId) {
if (mAlivcLivePusher != null) {
return mAlivcLivePusher.removeExternalAudioStream(streamId);
}
return -1;
}
// 输入外部音频流数据
public int pushExternalAudioStream(int streamId, AlivcLivePushAudioFrame audioFrame) {
if (mAlivcLivePusher != null) {
return mAlivcLivePusher.pushExternalAudioStream(streamId, audioFrame);
}
return -1;
}
// 设置是否与麦克风采集音频混合
public int setMixedWithMic(boolean mixed) {
if (mAlivcLivePusher != null) {
return mAlivcLivePusher.setMixedWithMic(mixed);
}
return -1;
}
// 设置外部音频流播放音量
public int setExternalAudioStreamPlayoutVolume(int streamId, int playoutVolume) {
if (mAlivcLivePusher != null) {
return mAlivcLivePusher.setExternalAudioStreamPlayoutVolume(streamId, playoutVolume);
}
return -1;
}
// 设置外部音频流推流音量
public int setExternalAudioStreamPublishVolume(int streamId, int publishVolume) {
if (mAlivcLivePusher != null) {
return mAlivcLivePusher.setExternalAudioStreamPublishVolume(streamId, publishVolume);
}
return -1;
}
// 启用外部视频输入源
public void setExternalVideoSource(boolean enable, boolean useTexture, AlivcLivePlayVideoStreamType videoStreamType, AlivcPreviewDisplayMode previewDisplayMode) {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.setExternalVideoSource(enable, useTexture, videoStreamType, previewDisplayMode);
}
}
// 外部视频输入/三方推流
public void inputStreamVideoData(byte[] data, int width, int height, int stride, int size, long pts, int rotation) {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.inputStreamVideoData(data, width, height, stride, size, pts, rotation);
}
}
// 外部音频输入/三方推流
public void inputStreamAudioData(byte[] data, int size, int sampleRate, int channels, long pts) {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.inputStreamAudioData(data, size, sampleRate, channels, pts);
}
}
// 输入外部视频流数据
public void pushExternalVideoFrame(AlivcLivePusherRawDataSample rawDataSample, AlivcLivePlayVideoStreamType videoStreamType) {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.pushExternalVideoFrame(rawDataSample, videoStreamType);
}
}
/**
* [END] ****************** 外部音视频输入/三方推流(参考示例) ******************
*/
/**
* ****************** 视频双流(参考示例) ****************** [START]
*/
// 开始屏幕共享流
public int startScreenShare() {
if (mAlivcLivePusher != null) {
return mAlivcLivePusher.startScreenShare();
}
return -1;
}
// 停止屏幕共享流
public int stopScreenShare() {
if (mAlivcLivePusher != null) {
return mAlivcLivePusher.stopScreenShare();
}
return -1;
}
/**
* [END] ****************** 视频双流(参考示例) ******************
*/
/**
* ****************** 音频双流(参考示例) ****************** [START]
*/
// 开启第二路音频流推送
public int startDualAudioStream() {
if (mAlivcLivePusher != null) {
return mAlivcLivePusher.startLocalDualAudioStream();
}
return -1;
}
// 停止第二路音频流推送
public int stopDualAudioStream() {
if (mAlivcLivePusher != null) {
return mAlivcLivePusher.stopLocalDualAudioStream();
}
return -1;
}
/**
* [END] ****************** 音频双流(参考示例) ******************
*/
}

View File

@@ -0,0 +1,241 @@
package com.alivc.live.interactive_common;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.acker.simplezxing.activity.CaptureActivity;
import com.alibaba.android.arouter.launcher.ARouter;
import com.alivc.live.commonbiz.SharedPreferenceUtils;
import com.alivc.live.commonui.utils.StatusBarUtil;
public class InteractiveAppInfoActivity extends AppCompatActivity {
public static final String FROM_EDITOR = "from_editor";
private static final int REQ_CODE_PERMISSION = 0x1111;
private ImageView mBackImageView;
private EditText mAppIdEditText;
private EditText mAppKeyEditText;
private EditText mPlayDomainEditText;
private TextView mConfirmTextView;
private ImageView mAppIdClearImageView;
private ImageView mAppKeyClearImageView;
private ImageView mPlayDomainClearImageView;
private boolean mFromEditor = false;
private int mSceneType = InteractiveConstants.SCENE_TYPE_INTERACTIVE_LIVE;
private EditText mCurrentEditText = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StatusBarUtil.translucent(this, Color.TRANSPARENT);
setContentView(R.layout.activity_interactive_app_info);
mFromEditor = getIntent().getBooleanExtra(FROM_EDITOR, false);
mSceneType = getIntent().getIntExtra(InteractiveConstants.DATA_TYPE_SCENE, InteractiveConstants.SCENE_TYPE_INTERACTIVE_LIVE);
initView();
initListener();
}
private void initView() {
mBackImageView = findViewById(R.id.iv_back);
mAppIdEditText = findViewById(R.id.et_app_id);
mAppKeyEditText = findViewById(R.id.et_app_key);
mPlayDomainEditText = findViewById(R.id.et_play_domain);
mAppIdClearImageView = findViewById(R.id.iv_app_id_clear);
mAppKeyClearImageView = findViewById(R.id.iv_app_key_clear);
mPlayDomainClearImageView = findViewById(R.id.iv_play_domain_clear);
mConfirmTextView = findViewById(R.id.tv_confirm);
}
private void initListener() {
mBackImageView.setOnClickListener((view) -> finish());
mAppIdEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
mAppIdClearImageView.setImageResource(TextUtils.isEmpty(editable.toString()) ? R.drawable.ic_scan_icon : R.drawable.ic_close);
changeConfirmTextView(checkEnable());
}
});
mAppKeyEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
mAppKeyClearImageView.setImageResource(TextUtils.isEmpty(editable.toString()) ? R.drawable.ic_scan_icon : R.drawable.ic_close);
changeConfirmTextView(checkEnable());
}
});
mPlayDomainEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
mPlayDomainClearImageView.setImageResource(TextUtils.isEmpty(editable.toString()) ? R.drawable.ic_scan_icon : R.drawable.ic_close);
changeConfirmTextView(checkEnable());
}
});
mAppIdClearImageView.setOnClickListener((view) -> checkForQrOrClear(mAppIdEditText));
mAppKeyClearImageView.setOnClickListener((view) -> checkForQrOrClear(mAppKeyEditText));
mPlayDomainClearImageView.setOnClickListener((view) -> checkForQrOrClear(mPlayDomainEditText));
mConfirmTextView.setOnClickListener((view) -> {
if (checkEnable()) {
String appId = mAppIdEditText.getText().toString();
String appKey = mAppKeyEditText.getText().toString();
String playDomain = mPlayDomainEditText.getText().toString();
SharedPreferenceUtils.setAppInfo(getApplicationContext(), appId, appKey, playDomain);
if (!mFromEditor) {
ARouter.getInstance()
.build("/interactiveCommon/liveInput")
.withInt(InteractiveConstants.DATA_TYPE_SCENE, mSceneType)
.navigation();
}
finish();
}
});
mAppIdEditText.setText(SharedPreferenceUtils.getAppId(getApplicationContext().getApplicationContext()));
mAppKeyEditText.setText(SharedPreferenceUtils.getAppKey(getApplicationContext().getApplicationContext()));
mPlayDomainEditText.setText(SharedPreferenceUtils.getPlayDomain(getApplicationContext().getApplicationContext()));
}
private void checkForQrOrClear(EditText editText) {
String content = editText.getText().toString().trim();
if (TextUtils.isEmpty(content)) {
mCurrentEditText = editText;
startQr();
} else {
editText.setText("");
}
}
private void changeConfirmTextView(boolean enable) {
if (enable) {
mConfirmTextView.setBackground(getResources().getDrawable(R.drawable.shape_pysh_btn_bg));
} else {
mConfirmTextView.setBackground(getResources().getDrawable(R.drawable.shape_rect_blue));
}
}
private boolean checkEnable() {
String appId = mAppIdEditText.getText().toString();
String appKey = mAppKeyEditText.getText().toString();
String playDomain = mPlayDomainEditText.getText().toString();
return !TextUtils.isEmpty(appId) && !TextUtils.isEmpty(appKey) && !TextUtils.isEmpty(playDomain);
}
private void startQr() {
if (ContextCompat.checkSelfPermission(InteractiveAppInfoActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
// Do not have the permission of camera, request it.
ActivityCompat.requestPermissions(InteractiveAppInfoActivity.this, new String[]{Manifest.permission.CAMERA}, REQ_CODE_PERMISSION);
} else {
// Have gotten the permission
startCaptureActivityForResult();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQ_CODE_PERMISSION: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// User agree the permission
startCaptureActivityForResult();
} else {
// User disagree the permission
Toast.makeText(this, "You must agree the camera permission request before you use the code scan function", Toast.LENGTH_LONG).show();
}
}
break;
default:
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case CaptureActivity.REQ_CODE:
switch (resultCode) {
case RESULT_OK:
if (mCurrentEditText != null) {
mCurrentEditText.setText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
}
break;
case RESULT_CANCELED:
if (data != null && mCurrentEditText != null) {
// for some reason camera is not working correctly
mCurrentEditText.setText(data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT));
}
break;
default:
break;
}
break;
default:
break;
}
}
private void startCaptureActivityForResult() {
Intent intent = new Intent(InteractiveAppInfoActivity.this, CaptureActivity.class);
Bundle bundle = new Bundle();
bundle.putBoolean(CaptureActivity.KEY_NEED_BEEP, CaptureActivity.VALUE_BEEP);
bundle.putBoolean(CaptureActivity.KEY_NEED_VIBRATION, CaptureActivity.VALUE_VIBRATION);
bundle.putBoolean(CaptureActivity.KEY_NEED_EXPOSURE, CaptureActivity.VALUE_NO_EXPOSURE);
bundle.putByte(CaptureActivity.KEY_FLASHLIGHT_MODE, CaptureActivity.VALUE_FLASHLIGHT_OFF);
bundle.putByte(CaptureActivity.KEY_ORIENTATION_MODE, CaptureActivity.VALUE_ORIENTATION_AUTO);
bundle.putBoolean(CaptureActivity.KEY_SCAN_AREA_FULL_SCREEN, CaptureActivity.VALUE_SCAN_AREA_FULL_SCREEN);
bundle.putBoolean(CaptureActivity.KEY_NEED_SCAN_HINT_TEXT, CaptureActivity.VALUE_SCAN_HINT_TEXT);
intent.putExtra(CaptureActivity.EXTRA_SETTING_BUNDLE, bundle);
startActivityForResult(intent, CaptureActivity.REQ_CODE);
}
}

View File

@@ -0,0 +1,57 @@
package com.alivc.live.interactive_common;
import com.alivc.live.player.annotations.AlivcLivePlayVideoStreamType;
import com.alivc.live.pusher.AlivcLiveMixSourceType;
import java.util.Random;
/**
* @author keria
* @date 2023/10/30
* @brief
*/
public class InteractiveBaseUtil {
private InteractiveBaseUtil() {
}
/**
* 生成随机房间号和用户id
*
* @param sceneType 场景直播连麦场景随机数0-100PK场景随机数100-999以此区分
* @param listener 回调结果
*/
public static void generateRandomId(int sceneType, OnRandomIdListener listener) {
Random random = new Random();
int randomNumber;
String channelId;
String userId;
if (sceneType == InteractiveConstants.SCENE_TYPE_INTERACTIVE_LIVE) {
randomNumber = random.nextInt(99) + 1;
String randomNumberStr = String.valueOf(randomNumber);
userId = randomNumberStr; // 互动直播的场景用户ID随机数范围 0-99
channelId = randomNumberStr; // 互动直播的场景房间ID随机数范围 0-99
} else {
randomNumber = random.nextInt(900) + 100; // 非互动直播的场景,随机数范围 0-999
String randomNumberStr = String.valueOf(randomNumber);
channelId = randomNumberStr;// 设置房间ID
userId = randomNumberStr;// 设置用户ID与房间ID相同
}
if (listener != null) {
listener.onResult(channelId, userId);
}
}
public static AlivcLiveMixSourceType covertVideoStreamType2MixSourceType(AlivcLivePlayVideoStreamType videoStreamType) {
if (videoStreamType == AlivcLivePlayVideoStreamType.STREAM_SCREEN) {
return AlivcLiveMixSourceType.SOURCE_SCREEN;
}
return AlivcLiveMixSourceType.SOURCE_CAMERA;
}
public interface OnRandomIdListener {
void onResult(String channelId, String userId);
}
}

View File

@@ -0,0 +1,17 @@
package com.alivc.live.interactive_common;
/**
* @author keria
* @date 2024/5/29
* @brief 互动直播常量类
*/
public @interface InteractiveConstants {
String DATA_TYPE_INTERACTIVE_USER_DATA = "user_data";
String DATA_TYPE_IS_ANCHOR = "IS_ANCHOR";
String DATA_TYPE_SCENE = "SCENE";
String KEY_TYPE_SCENE_MULTI_PK_16IN = "scene_multi_pk_16in";
int SCENE_TYPE_INTERACTIVE_LIVE = 0;
int SCENE_TYPE_PK_LIVE = 1;
}

View File

@@ -0,0 +1,294 @@
package com.alivc.live.interactive_common;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.acker.simplezxing.activity.CaptureActivity;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.alibaba.android.arouter.launcher.ARouter;
import com.alivc.live.commonbiz.backdoor.BackDoorInstance;
import com.alivc.live.commonbiz.test.AliLiveStreamURLUtil;
import com.alivc.live.commonui.utils.StatusBarUtil;
import com.alivc.live.commonutils.TextFormatUtil;
import com.alivc.live.commonutils.ToastUtils;
import com.alivc.live.interactive_common.bean.InteractiveUserData;
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
import com.alivc.live.interactive_common.widget.InteractLiveRadioButton;
import java.util.HashMap;
import java.util.regex.Pattern;
/**
* @author keria
* @date 2024/5/29
* @brief 互动直播房间号/用户ID输入页面
*/
@Route(path = "/interactiveCommon/liveInput")
public class InteractiveInputActivity extends AppCompatActivity {
private EditText mUserIdEdt;
private EditText mRoomIdEdt;
private ImageView mUserIdClearIv;
private ImageView mRoomIdClearIv;
private TextView mURLEntranceTv;
private TextView mInteractiveURLTv;
private TextView mSettingTv;
private TextView mConfirmTv;
private ImageView mBackIv;
// 直播连麦场景下的主播/观众角色选取
private InteractLiveRadioButton mAnchorRb;
private InteractLiveRadioButton mAudienceRb;
// 场景类型PK直播 or 连麦直播
private int mSceneType = InteractiveConstants.SCENE_TYPE_INTERACTIVE_LIVE;
private boolean isAnchor = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StatusBarUtil.translucent(this, Color.TRANSPARENT);
setContentView(R.layout.activity_interactive_input);
// 获取场景类型
mSceneType = getIntent().getIntExtra(InteractiveConstants.DATA_TYPE_SCENE, InteractiveConstants.SCENE_TYPE_INTERACTIVE_LIVE);
initView();
initListener();
}
private void initView() {
mUserIdEdt = findViewById(R.id.et_user_id);
mRoomIdEdt = findViewById(R.id.et_room_id);
mUserIdClearIv = findViewById(R.id.iv_user_id_clear);
mRoomIdClearIv = findViewById(R.id.iv_room_id_clear);
mURLEntranceTv = findViewById(R.id.tv_url_entrance);
mInteractiveURLTv = findViewById(R.id.tv_interactive_url);
// Update page title
TextView titleTv = findViewById(R.id.tv_title);
titleTv.setText(mSceneType == InteractiveConstants.SCENE_TYPE_PK_LIVE ? getString(R.string.pk_live) : getString(R.string.interact_live));
mSettingTv = findViewById(R.id.tv_setting);
mBackIv = findViewById(R.id.iv_back);
mConfirmTv = findViewById(R.id.tv_confirm);
View roleContainer = findViewById(R.id.role_container);
roleContainer.setVisibility(mSceneType == InteractiveConstants.SCENE_TYPE_PK_LIVE ? View.GONE : View.VISIBLE);
mAnchorRb = findViewById(R.id.radio_button_anchor);
mAnchorRb.setChecked(true);
mAudienceRb = findViewById(R.id.radio_button_audience);
}
private void initListener() {
InteractLiveAppInfoFragment interactLiveAppInfoFragment = (InteractLiveAppInfoFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_app_info);
if (interactLiveAppInfoFragment != null) {
interactLiveAppInfoFragment.setOnEditClickListener(new InteractLiveAppInfoFragment.OnEditClickListener() {
@Override
public void onLongClickAppInfoForDebug() {
InteractiveBaseUtil.generateRandomId(mSceneType, (channelId, userId) -> {
if (TextUtils.isEmpty(mRoomIdEdt.getText())) {
mRoomIdEdt.setText(channelId);
}
if (TextUtils.isEmpty(mUserIdEdt.getText())) {
mUserIdEdt.setText(userId);
}
});
}
@Override
public void onEditClickListener() {
Intent intent = new Intent(InteractiveInputActivity.this, InteractiveAppInfoActivity.class);
intent.putExtra(InteractiveAppInfoActivity.FROM_EDITOR, true);
intent.putExtra(InteractiveConstants.DATA_TYPE_SCENE, mSceneType);
startActivity(intent);
}
});
}
mBackIv.setOnClickListener(view -> finish());
mURLEntranceTv.setOnClickListener(v -> {
startCaptureActivityForResult();
});
mSettingTv.setOnClickListener(view -> {
Intent intent = new Intent(this, InteractiveSettingActivity.class);
startActivity(intent);
});
mUserIdEdt.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
changeConfirmTextView(checkEnable());
}
});
mRoomIdEdt.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
changeConfirmTextView(checkEnable());
}
});
mAnchorRb.setOnClickListener(view -> {
isAnchor = true;
mAnchorRb.setChecked(true);
mAudienceRb.setChecked(false);
});
mAudienceRb.setOnClickListener(view -> {
isAnchor = false;
mAnchorRb.setChecked(false);
mAudienceRb.setChecked(true);
});
mUserIdClearIv.setOnClickListener(view -> mUserIdEdt.setText(""));
mRoomIdClearIv.setOnClickListener(view -> mRoomIdEdt.setText(""));
mConfirmTv.setOnClickListener(view -> {
if (checkEnable()) {
startInteractiveActivity();
}
});
}
private void changeConfirmTextView(boolean enable) {
if (enable) {
mConfirmTv.setBackground(getResources().getDrawable(R.drawable.shape_pysh_btn_bg));
} else {
mConfirmTv.setBackground(getResources().getDrawable(R.drawable.shape_rect_blue));
}
}
private boolean checkEnable() {
String url = mInteractiveURLTv.getText().toString();
if (!TextUtils.isEmpty(url)) {
return true;
}
String userId = mUserIdEdt.getText().toString();
String roomId = mRoomIdEdt.getText().toString();
return !TextUtils.isEmpty(userId) && !TextUtils.isEmpty(roomId) && Pattern.matches(TextFormatUtil.REGULAR, userId) && Pattern.matches(TextFormatUtil.REGULAR, roomId);
}
private void startInteractiveActivity() {
String schema;
if (mSceneType == InteractiveConstants.SCENE_TYPE_PK_LIVE) {
schema = LivePushGlobalConfig.IS_MULTI_INTERACT
? "/interactivePK/multiPKLive"
: "/interactivePK/pkLive";
} else {
schema = LivePushGlobalConfig.IS_MULTI_INTERACT
? "/interactiveLive/multiInteractLive"
: "/interactiveLive/interactLive";
}
ARouter.getInstance()
.build(schema)
.withSerializable(InteractiveConstants.DATA_TYPE_INTERACTIVE_USER_DATA, generateUserData())
.withBoolean(InteractiveConstants.DATA_TYPE_IS_ANCHOR, isAnchor)
.withBoolean(InteractiveConstants.KEY_TYPE_SCENE_MULTI_PK_16IN, BackDoorInstance.getInstance().isUseMultiPK16IN())
.navigation();
}
private void startCaptureActivityForResult() {
Intent intent = new Intent(InteractiveInputActivity.this, CaptureActivity.class);
startActivityForResult(intent, CaptureActivity.REQ_CODE);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CaptureActivity.REQ_CODE) {
if (resultCode == RESULT_OK) {
String pushUrl = data.getStringExtra(CaptureActivity.EXTRA_SCAN_RESULT);
if (TextUtils.isEmpty(pushUrl)) {
return;
}
mInteractiveURLTv.setText(pushUrl);
clearInputUserInfo(true);
changeConfirmTextView(checkEnable());
checkPushURLSDKAppID(pushUrl);
}
}
}
// 清除输入的用户信息
private void clearInputUserInfo(boolean useUrl) {
if (useUrl) {
mUserIdEdt.setText("");
mRoomIdEdt.setText("");
} else {
mInteractiveURLTv.setText("");
}
}
// 如果当前以URL方式输入的 sdkAppId 和 demo 当前环境使用的 sdkAppId 不一致,则提示 toast以免业务异常
private void checkPushURLSDKAppID(String pushURL) {
if (TextUtils.isEmpty(pushURL)) {
return;
}
HashMap<String, String> params = AliLiveStreamURLUtil.parseUrl(pushURL);
String sdkAppId = params.get(AliLiveStreamURLUtil.SDK_APP_ID);
if (!TextUtils.equals(sdkAppId, AliLiveStreamURLUtil.APP_ID)) {
ToastUtils.show(getString(R.string.tips_interactive_url_use_different_sdkappid));
}
}
/**
* 生成 user data里面存有房间号用户id实际推流地址等信息
*
* @return user data
*/
private InteractiveUserData generateUserData() {
String channelId = mRoomIdEdt.getText().toString();
String userId = mUserIdEdt.getText().toString();
String pushURL = mInteractiveURLTv.getText().toString();
InteractiveUserData userData = new InteractiveUserData();
if (!TextUtils.isEmpty(channelId) && !TextUtils.isEmpty(userId)) {
userData.userId = userId;
userData.channelId = channelId;
userData.url = AliLiveStreamURLUtil.generateInteractivePushUrl(channelId, userId);
} else if (!TextUtils.isEmpty(pushURL)) {
HashMap<String, String> params = AliLiveStreamURLUtil.parseUrl(pushURL);
userData.channelId = AliLiveStreamURLUtil.parseURLStreamName(params);
userData.userId = params.get(AliLiveStreamURLUtil.USER_ID);
userData.url = pushURL;
}
return userData;
}
}

View File

@@ -0,0 +1,70 @@
package com.alivc.live.interactive_common;
/**
* @author keria
* @date 2023/10/27
* @brief DEMO互动模式业务类型
*/
public enum InteractiveMode {
/**
* 1v1连麦
*/
INTERACTIVE(0x10),
/**
* 多人连麦
*/
MULTI_INTERACTIVE(0x11),
/**
* 1v1 PK
*/
PK(0x20),
/**
* 多人PK
*/
MULTI_PK(0x21),
/**
* 1v1推拉裸流
*/
BARE_STREAM(0x30),
/**
* 多人推拉裸流
*/
MULTI_BARE_STREAM(0x31),
;
private final int mode;
InteractiveMode(int mode) {
this.mode = mode;
}
/**
* 是否是直播连麦(先推后拉)
*
* @param interactiveMode 互动模式业务类型
* @return true->是false->否
*/
public static boolean isInteractive(InteractiveMode interactiveMode) {
return interactiveMode == INTERACTIVE
|| interactiveMode == MULTI_INTERACTIVE
|| interactiveMode == PK
|| interactiveMode == MULTI_PK;
}
/**
* 是否是推拉裸流(直推直拉)
*
* @param interactiveMode 互动模式业务类型
* @return true->是false->否
*/
public static boolean isBareStream(InteractiveMode interactiveMode) {
return interactiveMode == BARE_STREAM
|| interactiveMode == MULTI_BARE_STREAM;
}
}

View File

@@ -0,0 +1,350 @@
package com.alivc.live.interactive_common;
import static com.alivc.live.interactive_common.utils.LivePushGlobalConfig.mAlivcLivePushConfig;
import android.app.DownloadManager;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.Switch;
import android.widget.TextView;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatActivity;
import com.alivc.live.annotations.AlivcLiveCameraCaptureOutputPreference;
import com.alivc.live.commonbiz.ResourcesDownload;
import com.alivc.live.commonbiz.SharedPreferenceUtils;
import com.alivc.live.commonui.configview.LivePushSettingView;
import com.alivc.live.commonui.utils.StatusBarUtil;
import com.alivc.live.commonui.widgets.AVLiveLoadingDialog;
import com.alivc.live.commonutils.DownloadUtil;
import com.alivc.live.commonutils.ToastUtils;
import com.alivc.live.interactive_common.utils.LivePushGlobalConfig;
import com.alivc.live.pusher.AlivcAudioChannelEnum;
import com.alivc.live.pusher.AlivcAudioSampleRateEnum;
import com.alivc.live.pusher.AlivcAudioSceneModeEnum;
import com.alivc.live.pusher.AlivcEncodeModeEnum;
import com.alivc.live.pusher.AlivcImageFormat;
import com.alivc.live.pusher.AlivcLivePushCameraTypeEnum;
import com.alivc.live.pusher.AlivcPreviewOrientationEnum;
import com.alivc.live.pusher.AlivcResolutionEnum;
import com.alivc.live.pusher.AlivcSoundFormat;
import java.io.File;
public class InteractiveSettingActivity extends AppCompatActivity {
private View mTabArgsLayout;
private View mTabActionLayout;
private View mTabArgsView;
private View mTabActionView;
private ImageView mBackImageView;
private Switch mMultiInteractSwitch;
private Switch mH5CompatibleSwitch;
private Switch mDataChannelSwitch;
private Switch mEarbackOpenWithoutHeadsetSwitch;
private TextView mCameraCaptureOutputPreferenceTv;
private Button mCommitButton;
private AVLiveLoadingDialog mLoadingDialog;
private LivePushSettingView mLivePushSettingView;
private AlivcResolutionEnum mCurrentResolution;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StatusBarUtil.translucent(this, Color.TRANSPARENT);
setContentView(R.layout.activity_interactive_setting);
initView();
initData();
initListener();
}
private void initView() {
mBackImageView = findViewById(R.id.iv_back);
mCommitButton = findViewById(R.id.btn_commit);
mMultiInteractSwitch = findViewById(R.id.multi_interaction_control);
mMultiInteractSwitch.setChecked(LivePushGlobalConfig.IS_MULTI_INTERACT);
mH5CompatibleSwitch = findViewById(R.id.h5_compatible_control);
mH5CompatibleSwitch.setChecked(LivePushGlobalConfig.IS_H5_COMPATIBLE);
mDataChannelSwitch = findViewById(R.id.data_channel_control);
mDataChannelSwitch.setChecked(LivePushGlobalConfig.IS_DATA_CHANNEL_MESSAGE_ENABLE);
mEarbackOpenWithoutHeadsetSwitch = findViewById(R.id.earback_open_without_headset_control);
mEarbackOpenWithoutHeadsetSwitch.setChecked(LivePushGlobalConfig.IS_EARBACK_OPEN_WITHOUT_HEADSET);
mCameraCaptureOutputPreferenceTv = findViewById(R.id.setting_camera_capture_output_preference);
String cameraCaptureOutputPreferenceText = getTextFromCameraCaptureOutputPreference(LivePushGlobalConfig.CAMERA_CAPTURE_OUTPUT_PREFERENCE);
mCameraCaptureOutputPreferenceTv.setText(cameraCaptureOutputPreferenceText);
mTabArgsLayout = findViewById(R.id.tab_args_layout);
mTabActionLayout = findViewById(R.id.tab_action_layout);
mTabArgsView = findViewById(R.id.tab_args_view);
mTabActionView = findViewById(R.id.tab_action_view);
mLivePushSettingView = findViewById(R.id.setting_view);
}
private void initData() {
mLivePushSettingView.setDefaultConfig(mAlivcLivePushConfig, LivePushGlobalConfig.ENABLE_BEAUTY, LivePushGlobalConfig.ENABLE_LOCAL_LOG, LivePushGlobalConfig.ENABLE_WATER_MARK);
}
private void initListener() {
mLivePushSettingView.bitrateControl.observe(this, isChecked -> {
mAlivcLivePushConfig.setEnableBitrateControl(isChecked);
});
mLivePushSettingView.targetVideoBitrate.observe(this, bitrate -> {
SharedPreferenceUtils.setHintTargetBit(getApplicationContext(), bitrate);
mAlivcLivePushConfig.setTargetVideoBitrate(bitrate);
});
mLivePushSettingView.minVideoBitrate.observe(this, bitrate -> {
SharedPreferenceUtils.setHintMinBit(getApplicationContext(), bitrate);
mAlivcLivePushConfig.setMinVideoBitrate(bitrate);
});
mLivePushSettingView.initialVideoBitrate.observe(this, bitrate -> {
SharedPreferenceUtils.setHintMinBit(getApplicationContext(), bitrate);
mAlivcLivePushConfig.setInitialVideoBitrate(bitrate);
});
mLivePushSettingView.audioBitrate.observe(this, bitrate -> {
SharedPreferenceUtils.setHintMinBit(getApplicationContext(), bitrate);
mAlivcLivePushConfig.setAudioBitRate(bitrate);
});
mLivePushSettingView.variableResolution.observe(this, isChecked -> {
mAlivcLivePushConfig.setEnableAutoResolution(isChecked);
});
mLivePushSettingView.minFps.observe(this, minFps -> {
mAlivcLivePushConfig.setMinFps(minFps);
});
mLivePushSettingView.audioSampleRate.observe(this, sampleRate -> {
mAlivcLivePushConfig.setAudioSampleRate(sampleRate);
});
mLivePushSettingView.gop.observe(this, gop -> {
mAlivcLivePushConfig.setVideoEncodeGop(gop);
});
mLivePushSettingView.fps.observe(this, fps -> {
mAlivcLivePushConfig.setFps(fps);
});
mLivePushSettingView.videoHardwareDecode.observe(this, isChecked -> {
mAlivcLivePushConfig.setVideoEncodeMode(isChecked ? AlivcEncodeModeEnum.Encode_MODE_HARD : AlivcEncodeModeEnum.Encode_MODE_SOFT);
});
mLivePushSettingView.audioHardwareDecode.observe(this, isChecked -> {
mAlivcLivePushConfig.setAudioEncodeMode(isChecked ? AlivcEncodeModeEnum.Encode_MODE_HARD : AlivcEncodeModeEnum.Encode_MODE_SOFT);
});
mLivePushSettingView.pushMirror.observe(this, isChecked -> {
mAlivcLivePushConfig.setPushMirror(isChecked);
});
mLivePushSettingView.previewMirror.observe(this, isChecked -> {
mAlivcLivePushConfig.setPreviewMirror(isChecked);
});
mLivePushSettingView.enableFrontCamera.observe(this, isChecked -> {
mAlivcLivePushConfig.setCameraType(isChecked ? AlivcLivePushCameraTypeEnum.CAMERA_TYPE_FRONT : AlivcLivePushCameraTypeEnum.CAMERA_TYPE_BACK);
// mCameraId = (isChecked ? AlivcLivePushCameraTypeEnum.CAMERA_TYPE_FRONT.getCameraId() : AlivcLivePushCameraTypeEnum.CAMERA_TYPE_BACK.getCameraId());
});
mLivePushSettingView.autoFocus.observe(this, isChecked -> {
mAlivcLivePushConfig.setAutoFocus(isChecked);
});
mLivePushSettingView.enableBeauty.observe(this, isChecked -> {
LivePushGlobalConfig.ENABLE_BEAUTY = isChecked;
});
mLivePushSettingView.videoOnly.observe(this, isChecked -> {
mAlivcLivePushConfig.setVideoOnly(isChecked);
});
mLivePushSettingView.audioOnly.observe(this, isChecked -> {
mAlivcLivePushConfig.setAudioOnly(isChecked);
});
mLivePushSettingView.pauseImage.observe(this, isChecked -> {
if (!isChecked) {
mAlivcLivePushConfig.setPausePushImage("");
} else {
if (mAlivcLivePushConfig.getPreviewOrientation() == AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_LEFT.getOrientation() || mAlivcLivePushConfig.getPreviewOrientation() == AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_RIGHT.getOrientation()) {
mAlivcLivePushConfig.setPausePushImage(getFilesDir().getPath() + File.separator + "alivc_resource/background_push_land.png");
} else {
mAlivcLivePushConfig.setPausePushImage(getFilesDir().getPath() + File.separator + "alivc_resource/background_push.png");
}
}
});
mLivePushSettingView.netWorkImage.observe(this, isChecked -> {
if (!isChecked) {
mAlivcLivePushConfig.setNetworkPoorPushImage("");
} else {
if (mAlivcLivePushConfig.getPreviewOrientation() == AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_LEFT.getOrientation() || mAlivcLivePushConfig.getPreviewOrientation() == AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_RIGHT.getOrientation()) {
mAlivcLivePushConfig.setNetworkPoorPushImage(getFilesDir().getPath() + File.separator + "alivc_resource/poor_network_land.png");
} else {
mAlivcLivePushConfig.setNetworkPoorPushImage(getFilesDir().getPath() + File.separator + "alivc_resource/poor_network.png");
}
}
});
mLivePushSettingView.musicMode.observe(this, isChecked -> {
mAlivcLivePushConfig.setAudioSceneMode(isChecked ? AlivcAudioSceneModeEnum.AUDIO_SCENE_MUSIC_MODE : AlivcAudioSceneModeEnum.AUDIO_SCENE_DEFAULT_MODE);
});
mLivePushSettingView.extern.observe(this, isChecked -> {
mAlivcLivePushConfig.setExternMainStream(isChecked, AlivcImageFormat.IMAGE_FORMAT_YUVNV12, AlivcSoundFormat.SOUND_FORMAT_S16);
mAlivcLivePushConfig.setAudioChannels(AlivcAudioChannelEnum.AUDIO_CHANNEL_ONE);
mAlivcLivePushConfig.setAudioSampleRate(AlivcAudioSampleRateEnum.AUDIO_SAMPLE_RATE_44100);
if (isChecked) {
startDownloadYUV();
}
});
mLivePushSettingView.localLog.observe(this, isChecked -> {
LivePushGlobalConfig.ENABLE_LOCAL_LOG = isChecked;
});
mLivePushSettingView.previewDisplayMode.observe(this, previewDisplayMode -> {
mAlivcLivePushConfig.setPreviewDisplayMode(previewDisplayMode);
});
mLivePushSettingView.cameraCaptureOutputPreference.observe(this, preference -> {
mAlivcLivePushConfig.setCameraCaptureOutputPreference(preference);
});
mLivePushSettingView.audioChannel.observe(this, audioChannel -> {
mAlivcLivePushConfig.setAudioChannels(audioChannel);
});
mLivePushSettingView.audioProfile.observe(this, audioProfile -> {
mAlivcLivePushConfig.setAudioProfile(audioProfile);
});
mLivePushSettingView.videoEncodeType.observe(this, videoEncodeType -> {
mAlivcLivePushConfig.setVideoEncodeType(videoEncodeType);
});
mLivePushSettingView.bFrame.observe(this, bFrame -> {
mAlivcLivePushConfig.setBFrames(bFrame);
});
mLivePushSettingView.previewOrientation.observe(this, orientation -> {
mAlivcLivePushConfig.setPreviewOrientation(orientation);
});
mLivePushSettingView.pauseImagePath.observe(this, path -> {
if (mAlivcLivePushConfig.getPausePushImage() == null || mAlivcLivePushConfig.getPausePushImage().equals("")) {
mAlivcLivePushConfig.setPausePushImage(path);
}
});
mLivePushSettingView.netWorkImagePath.observe(this, path -> {
if (mAlivcLivePushConfig.getNetworkPoorPushImage() != null && !mAlivcLivePushConfig.getNetworkPoorPushImage().equals("")) {
mAlivcLivePushConfig.setNetworkPoorPushImage(path);
}
});
mLivePushSettingView.qualityMode.observe(this, quality -> {
mAlivcLivePushConfig.setQualityMode(quality);
});
mLivePushSettingView.resolution.observe(this, resolution -> {
this.mCurrentResolution = resolution;
if (resolution != AlivcResolutionEnum.RESOLUTION_SELF_DEFINE) {
mAlivcLivePushConfig.setResolution(resolution);
}
});
mTabArgsLayout.setOnClickListener(view -> {
mTabArgsView.setVisibility(View.VISIBLE);
mTabActionView.setVisibility(View.INVISIBLE);
mLivePushSettingView.showArgsContent(true);
});
mTabActionLayout.setOnClickListener(view -> {
mTabActionView.setVisibility(View.VISIBLE);
mTabArgsView.setVisibility(View.INVISIBLE);
mLivePushSettingView.showArgsContent(false);
});
mBackImageView.setOnClickListener(view -> {
finish();
});
mCommitButton.setOnClickListener(view -> {
if (mCurrentResolution == AlivcResolutionEnum.RESOLUTION_SELF_DEFINE) {
AlivcResolutionEnum.RESOLUTION_SELF_DEFINE.setSelfDefineResolution(mLivePushSettingView.getSelfDefineResolutionWidth(), mLivePushSettingView.getSelfDefineResolutionHeight());
mAlivcLivePushConfig.setResolution(AlivcResolutionEnum.RESOLUTION_SELF_DEFINE);
}
mAlivcLivePushConfig.setConnectRetryCount(mLivePushSettingView.getRetryCount());
mAlivcLivePushConfig.setConnectRetryInterval(mLivePushSettingView.getRetryInterval());
finish();
});
mMultiInteractSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
LivePushGlobalConfig.IS_MULTI_INTERACT = isChecked;
}
});
mH5CompatibleSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
LivePushGlobalConfig.IS_H5_COMPATIBLE = isChecked;
}
});
mDataChannelSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
LivePushGlobalConfig.IS_DATA_CHANNEL_MESSAGE_ENABLE = isChecked;
}
});
mEarbackOpenWithoutHeadsetSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
LivePushGlobalConfig.IS_EARBACK_OPEN_WITHOUT_HEADSET = isChecked;
}
});
}
private void showProgressDialog(@StringRes int tipsResId) {
if (null == mLoadingDialog) {
mLoadingDialog = new AVLiveLoadingDialog(this).tip(getString(tipsResId));
}
if (!mLoadingDialog.isShowing()) {
mLoadingDialog.show();
}
}
private void hideProgressDialog() {
if (mLoadingDialog != null && mLoadingDialog.isShowing()) {
mLoadingDialog.dismiss();
mLoadingDialog = null;
}
}
private void startDownloadYUV() {
long mCaptureDownloadId = ResourcesDownload.downloadCaptureYUV(this, new DownloadUtil.OnDownloadListener() {
@Override
public void onDownloadSuccess(long downloadId) {
hideProgressDialog();
ToastUtils.show("Download Success");
}
@Override
public void onDownloadProgress(long downloadId, double percent) {
}
@Override
public void onDownloadError(long downloadId, int errorCode, String errorMsg) {
hideProgressDialog();
ToastUtils.show(errorMsg);
if (errorCode != DownloadManager.ERROR_FILE_ALREADY_EXISTS) {
mLivePushSettingView.externDownloadError();
}
}
});
showProgressDialog(R.string.waiting_download_video_resources);
}
private String getTextFromCameraCaptureOutputPreference(AlivcLiveCameraCaptureOutputPreference cameraCaptureOutputPreference) {
if (cameraCaptureOutputPreference == AlivcLiveCameraCaptureOutputPreference.AUTO) {
return getString(R.string.camera_capture_output_preference_auto);
} else if (cameraCaptureOutputPreference == AlivcLiveCameraCaptureOutputPreference.PERFORMANCE) {
return getString(R.string.camera_capture_output_preference_performance);
} else if (cameraCaptureOutputPreference == AlivcLiveCameraCaptureOutputPreference.PREVIEW) {
return getString(R.string.camera_capture_output_preference_preview);
} else {
return getString(R.string.camera_capture_output_preference_preview);
}
}
}

View File

@@ -0,0 +1,141 @@
package com.alivc.live.interactive_common.bean;
import android.content.Context;
import android.util.Log;
import com.alivc.live.annotations.AlivcLiveMode;
import com.alivc.live.annotations.AlivcLiveNetworkQuality;
import com.alivc.live.commonutils.ToastUtils;
import com.alivc.live.interactive_common.listener.InteractLivePushPullListener;
import com.alivc.live.player.AlivcLivePlayInfoListener;
import com.alivc.live.player.AlivcLivePlayerImpl;
import com.alivc.live.player.AlivcLivePlayerStatsInfo;
import com.alivc.live.player.annotations.AlivcLivePlayError;
/**
* AlivcLivePlayer 封装类,用于直播连麦拉流
*/
public class InteractiveLivePlayer extends AlivcLivePlayerImpl {
private static final String TAG = InteractiveLivePlayer.class.getSimpleName();
private InteractLivePushPullListener mListener;
private boolean mIsPulling = false;
private final AlivcLivePlayInfoListener mAlivcLivePlayInfoListener = new AlivcLivePlayInfoListener() {
@Override
public void onPlayStarted() {
Log.i(TAG, "onPlayStarted: " + mPullUserData);
mIsPulling = true;
if (mListener != null) {
mListener.onPullSuccess(mPullUserData);
}
}
@Override
public void onPlayStopped() {
Log.i(TAG, "onPlayStopped: " + mPullUserData);
notifyPullStop();
}
@Override
public void onFirstVideoFrameDrawn() {
}
@Override
public void onNetworkQualityChanged(AlivcLiveNetworkQuality upQuality, AlivcLiveNetworkQuality downQuality) {
Log.w(TAG, "onNetworkQualityChanged: " + upQuality + ", " + downQuality);
}
@Override
public void onReceiveSEIMessage(int payload, byte[] data) {
//Log.d(TAG, "onReceiveSEIMessage: " + payload + ", " + new String(data, StandardCharsets.UTF_8));
if (mListener != null) {
mListener.onReceiveSEIMessage(payload, data);
}
}
@Override
public void onReceiveDataChannelMessage(byte[] data) {
super.onReceiveDataChannelMessage(data);
//Log.d(TAG, "onReceiveDataChannelMessage: " + new String(data, StandardCharsets.UTF_8));
}
@Override
public void onPlayoutVolumeUpdate(int volume, boolean isSpeaking) {
// 音频音量(仅互动模式下生效需设置AlivcLivePusher#enableAudioVolumeIndication接口)
// Log.d(TAG, "onPlayoutVolumeUpdate: " + volume + ", " + isSpeaking);
}
@Override
public void onAudioMuted(boolean mute) {
ToastUtils.show("onAudioMuted: " + mute);
}
@Override
public void onVideoMuted(boolean mute) {
ToastUtils.show("onVideoMuted: " + mute);
}
@Override
public void onVideoEnabled(boolean enable) {
ToastUtils.show("onVideoEnabled: " + enable);
if (mListener != null) {
mListener.onVideoEnabled(enable);
}
}
@Override
public void onVideoResolutionChanged(int width, int height) {
}
@Override
public void onError(AlivcLivePlayError alivcLivePlayError, String s) {
Log.e(TAG, "onError: " + mPullUserData + ", " + alivcLivePlayError + ", " + s);
mIsPulling = false;
if (mListener != null) {
mListener.onPullError(mPullUserData, alivcLivePlayError, s);
}
}
@Override
public void onPlayerStatistics(AlivcLivePlayerStatsInfo statsInfo) {
//Log.i(TAG, "onPlayerStatistics: " + statsInfo);
}
};
private InteractiveUserData mPullUserData;
public InteractiveLivePlayer(Context context, AlivcLiveMode mode) {
super(context, mode);
setPlayInfoListener(mAlivcLivePlayInfoListener);
}
public void setMultiInteractPlayInfoListener(InteractLivePushPullListener listener) {
this.mListener = listener;
}
public boolean isPulling() {
return mIsPulling;
}
public void setPullUserData(InteractiveUserData userData) {
this.mPullUserData = userData;
}
// ugly codes: fix the issue of async callbacks not being executed in stopPlay after executing destroy immediately
@Override
public void destroy() {
notifyPullStop();
setPlayInfoListener(null);
mListener = null;
super.destroy();
}
private void notifyPullStop() {
mIsPulling = false;
if (mListener != null) {
mListener.onPullStop(mPullUserData);
}
}
}

View File

@@ -0,0 +1,42 @@
package com.alivc.live.interactive_common.bean;
import android.text.TextUtils;
import android.util.Log;
import com.alivc.live.player.annotations.AlivcLivePlayAudioStreamType;
import com.alivc.live.player.annotations.AlivcLivePlayVideoStreamType;
import java.io.Serializable;
/**
* @author keria
* @date 2023/10/18
* @note 直播连麦推拉流用户信息
*/
public class InteractiveUserData implements Serializable {
private static final String TAG = InteractiveUserData.class.getSimpleName();
public String channelId;
public String userId;
public AlivcLivePlayVideoStreamType videoStreamType;
public AlivcLivePlayAudioStreamType audioStreamType;
public String url;
public String getKey() {
if (!TextUtils.isEmpty(channelId) && !TextUtils.isEmpty(userId)) {
return String.format("%s_%s_%s", channelId, userId, videoStreamType == AlivcLivePlayVideoStreamType.STREAM_SCREEN ? "screen" : "camera");
} else if (!TextUtils.isEmpty(url)) {
return url;
}
Log.e(TAG, "invalid interactive user data");
return null;
}
@Override
public String toString() {
return getKey();
}
}

View File

@@ -0,0 +1,5 @@
package com.alivc.live.interactive_common.listener;
public interface ConnectionLostListener {
void onConfirm();
}

View File

@@ -0,0 +1,17 @@
package com.alivc.live.interactive_common.listener;
import com.alivc.live.interactive_common.bean.InteractiveUserData;
import com.alivc.live.player.annotations.AlivcLivePlayError;
public abstract class InteractLivePushPullListener {
public void onPullSuccess(InteractiveUserData userData) {}
public void onPullError(InteractiveUserData userData, AlivcLivePlayError errorType, String errorMsg) {}
public void onPullStop(InteractiveUserData userData) {}
public void onPushSuccess(){}
public void onPushError(){}
public void onVideoEnabled(boolean enable){}
public void onConnectionLost(){}
public void onReceiveSEIMessage(int payload, byte[] data) {}
public void onPlayerSei(int payload, byte[] uuid, byte[] data) {}
public void onReceiveSEIDelay(String src, String type, String msg) {}
}

View File

@@ -0,0 +1,10 @@
package com.alivc.live.interactive_common.listener;
import com.alivc.live.interactive_common.bean.InteractiveUserData;
public abstract class InteractLiveTipsViewListener {
public void onCancel() {}
public void onConfirm() {}
public void onInputConfirm(InteractiveUserData userData) {}
public void onQrClick() {}
}

View File

@@ -0,0 +1,67 @@
package com.alivc.live.interactive_common.manager;
import android.graphics.Bitmap;
import android.os.Handler;
import com.alivc.live.commonutils.BitmapUtil;
import com.alivc.live.commonutils.ContextUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author keria
* @date 2023/9/15
* @brief 时间戳文字水印管理类
* @note 每秒生成一帧文字水印的bitmap回调给外层进行添加水印处理
*/
public class TimestampWatermarkManager {
private static final long TIME_INTERVAL = 1000L;
private final Handler sHandler = new Handler();
private final Runnable sRunnable = new Runnable() {
@Override
public void run() {
processEvent();
sHandler.postDelayed(this, TIME_INTERVAL);
}
};
private OnWatermarkListener mOnWatermarkListener;
public void init(OnWatermarkListener listener) {
mOnWatermarkListener = listener;
startHandler();
}
public void destroy() {
removeHandler();
mOnWatermarkListener = null;
}
public void processEvent() {
if (mOnWatermarkListener != null) {
String timestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
Bitmap bitmap = BitmapUtil.createTextBitmap(ContextUtils.getContext(), timestamp);
mOnWatermarkListener.onWatermarkUpdate(bitmap);
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
bitmap = null;
}
}
}
private void startHandler() {
sHandler.postDelayed(sRunnable, TIME_INTERVAL);
}
private void removeHandler() {
sHandler.removeCallbacksAndMessages(null);
}
public interface OnWatermarkListener {
public void onWatermarkUpdate(Bitmap bitmap);
}
}

View File

@@ -0,0 +1,17 @@
package com.alivc.live.interactive_common.utils;
public enum InteractLiveIntent {
/**
* 关闭页面
*/
INTENT_FINISH,
/**
* 停止拉流
*/
INTENT_STOP_PULL,
/**
*
* 停止推流
*/
INTENT_STOP_PUSH
}

View File

@@ -0,0 +1,56 @@
package com.alivc.live.interactive_common.utils;
import com.alivc.live.annotations.AlivcLiveCameraCaptureOutputPreference;
import com.alivc.live.pusher.AlivcLivePushConfig;
/**
* 直播连麦公共配置
*
* @note 用于传递配置页的一些公共参数到直播连麦页面
*/
public class LivePushGlobalConfig {
/**
* 推流配置
*/
public static AlivcLivePushConfig mAlivcLivePushConfig = new AlivcLivePushConfig();
/**
* 多人互动模式/PK
*/
public static boolean IS_MULTI_INTERACT = false;
/**
* 开启 Data Channel 自定义消息通道
*/
public static boolean IS_DATA_CHANNEL_MESSAGE_ENABLE = false;
/**
* 开启强制耳返
*/
public static boolean IS_EARBACK_OPEN_WITHOUT_HEADSET = false;
/**
* H5兼容模式可与web连麦互通
*/
public static boolean IS_H5_COMPATIBLE = false;
/**
* 美颜
*/
public static boolean ENABLE_BEAUTY = true;
/**
* 本地日志
*/
public static boolean ENABLE_LOCAL_LOG = true;
/**
* 水印
*/
public static boolean ENABLE_WATER_MARK = false;
/**
* 摄像头采集偏好
*/
public static AlivcLiveCameraCaptureOutputPreference CAMERA_CAPTURE_OUTPUT_PREFERENCE = AlivcLiveCameraCaptureOutputPreference.PREVIEW;
}

View File

@@ -0,0 +1,66 @@
package com.alivc.live.interactive_common.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.alivc.live.commonui.avdialog.AUILiveDialog;
import com.alivc.live.interactive_common.R;
import com.alivc.live.interactive_common.listener.ConnectionLostListener;
public class ConnectionLostTipsView extends ConstraintLayout {
private final Context mContext;
private final View inflate;
private TextView mConfirmTextView;
private AUILiveDialog mAUILiveDialog;
private ConnectionLostListener mConnectionLostListener;
public ConnectionLostTipsView(@NonNull Context context) {
this(context,null);
}
public ConnectionLostTipsView(@NonNull Context context, AttributeSet attrs) {
this(context, attrs,-1);
}
public ConnectionLostTipsView(@NonNull Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
mAUILiveDialog = new AUILiveDialog(context);
inflate = LayoutInflater.from(mContext).inflate(R.layout.layout_connect_lost_tips_view, this, true);
initView();
initListener();
mAUILiveDialog.setContentView(this);
}
private void initView(){
mConfirmTextView = inflate.findViewById(R.id.tv_confirm);
}
private void initListener(){
mConfirmTextView.setOnClickListener(view -> {
if(mConnectionLostListener != null){
mConnectionLostListener.onConfirm();
}
});
}
public void show(){
mAUILiveDialog.show();
}
public void hide(){
mAUILiveDialog.dismiss();
}
public void setConnectionLostListener(ConnectionLostListener listener){
this.mConnectionLostListener = listener;
}
}

View File

@@ -0,0 +1,57 @@
package com.alivc.live.interactive_common.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.alivc.live.interactive_common.R;
public class InteractLiveRadioButton extends ConstraintLayout {
private final Context mContext;
private ConstraintLayout mConstraintLayoutRootView;
private TextView mTitleTextView;
public InteractLiveRadioButton(Context context) {
this(context, null);
}
public InteractLiveRadioButton(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public InteractLiveRadioButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
initView();
initAttribute(attrs);
}
private void initView() {
View mRootView = LayoutInflater.from(mContext).inflate(R.layout.layout_interact_live_radio_button, this, true);
mConstraintLayoutRootView = mRootView.findViewById(R.id.root);
mTitleTextView = mRootView.findViewById(R.id.tv_title);
}
private void initAttribute(AttributeSet attrs) {
TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.InteractLiveRadioButton);
String mText = typedArray.getString(R.styleable.InteractLiveRadioButton_ilrb_text);
typedArray.recycle();
mTitleTextView.setText(mText);
}
public void setChecked(boolean isChecked) {
if (isChecked) {
mConstraintLayoutRootView.setBackground(getResources().getDrawable(R.drawable.shape_interact_live_selected));
} else {
mConstraintLayoutRootView.setBackground(getResources().getDrawable(R.drawable.shape_interact_live_unselected));
}
}
}

View File

@@ -0,0 +1,217 @@
package com.alivc.live.interactive_common.widget;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.alivc.live.interactive_common.R;
import com.alivc.live.interactive_common.bean.InteractiveUserData;
import com.alivc.live.interactive_common.listener.InteractLiveTipsViewListener;
import com.alivc.live.player.annotations.AlivcLivePlayAudioStreamType;
import com.alivc.live.player.annotations.AlivcLivePlayVideoStreamType;
/**
* 直播连麦用户信息输入框
*/
public class InteractiveCommonInputView extends ConstraintLayout {
public enum ViewType {
INTERACTIVE,
PK,
BARE_STREAM
}
private ViewType mViewType;
private final Context mContext;
private final View inflate;
private TextView mCancelTextView;
private TextView mConfirmTextView;
private TextView mTipsTextView;
private EditText mInputUserIdEditText;
private EditText mInputRoomIdEditText;
private EditText mInputUrlEditText;
private ImageView mClearUserIdImageView;
private ImageView mClearRoomIdImageView;
private ImageView mScanQrImageView;
private LinearLayout mVideoRadioGroupLayout;
private RadioGroup mVideoStreamTypeRadioGroup;
private RadioButton mVideoCamaraRadioButton;
private RadioButton mVideoScreenRadioButton;
private LinearLayout mAudioRadioGroupLayout;
private RadioGroup mAudioStreamTypeRadioGroup;
private RadioButton mAudioMicRadioButton;
private RadioButton mAudioDualRadioButton;
private InteractLiveTipsViewListener mInteractLiveTipsViewListener;
private boolean mShowInputView;
public InteractiveCommonInputView(@NonNull Context context) {
this(context, null);
}
public InteractiveCommonInputView(@NonNull Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public InteractiveCommonInputView(@NonNull Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
inflate = LayoutInflater.from(mContext).inflate(R.layout.layout_interactive_common_input, this, true);
initView();
initListener();
}
private void initView() {
mCancelTextView = inflate.findViewById(R.id.tv_cancel);
mConfirmTextView = inflate.findViewById(R.id.tv_confirm);
mTipsTextView = inflate.findViewById(R.id.tv_tips);
mInputUserIdEditText = inflate.findViewById(R.id.et_input_user_id);
mInputRoomIdEditText = inflate.findViewById(R.id.et_input_room_id);
mInputUrlEditText = inflate.findViewById(R.id.et_input_url);
mClearUserIdImageView = inflate.findViewById(R.id.iv_clear_user_id);
mClearRoomIdImageView = inflate.findViewById(R.id.iv_clear_room_id);
mScanQrImageView = inflate.findViewById(R.id.iv_scan_qr);
mVideoRadioGroupLayout = inflate.findViewById(R.id.video_radio_group);
mVideoStreamTypeRadioGroup = inflate.findViewById(R.id.rg_video_stream_type);
mVideoCamaraRadioButton = inflate.findViewById(R.id.rb_camera);
mVideoScreenRadioButton = inflate.findViewById(R.id.rb_screen);
mAudioRadioGroupLayout = inflate.findViewById(R.id.audio_radio_group);
mAudioStreamTypeRadioGroup = inflate.findViewById(R.id.rg_audio_stream_type);
mAudioMicRadioButton = inflate.findViewById(R.id.rb_mic);
mAudioDualRadioButton = inflate.findViewById(R.id.rb_dual);
}
private void initListener() {
mCancelTextView.setOnClickListener(view -> {
if (mInteractLiveTipsViewListener != null) {
mInteractLiveTipsViewListener.onCancel();
}
});
mConfirmTextView.setOnClickListener(view -> {
if (mInteractLiveTipsViewListener == null) {
return;
}
if (!mShowInputView) {
mInteractLiveTipsViewListener.onConfirm();
return;
}
InteractiveUserData inputData = new InteractiveUserData();
if (mViewType == ViewType.INTERACTIVE) {
inputData.userId = mInputUserIdEditText.getText().toString().trim();
} else if (mViewType == ViewType.PK) {
inputData.userId = mInputUserIdEditText.getText().toString().trim();
inputData.channelId = mInputRoomIdEditText.getText().toString().trim();
} else if (mViewType == ViewType.BARE_STREAM) {
inputData.url = mInputUrlEditText.getText().toString().trim();
}
inputData.videoStreamType = (mVideoStreamTypeRadioGroup.getCheckedRadioButtonId() == mVideoCamaraRadioButton.getId())
? AlivcLivePlayVideoStreamType.STREAM_CAMERA
: AlivcLivePlayVideoStreamType.STREAM_SCREEN;
inputData.audioStreamType = (mAudioStreamTypeRadioGroup.getCheckedRadioButtonId() == mAudioMicRadioButton.getId())
? AlivcLivePlayAudioStreamType.STREAM_MIC
: AlivcLivePlayAudioStreamType.STREAM_DUAL;
mInteractLiveTipsViewListener.onInputConfirm(inputData);
});
mClearUserIdImageView.setOnClickListener(view -> mInputUserIdEditText.setText(""));
mClearRoomIdImageView.setOnClickListener(view -> mInputRoomIdEditText.setText(""));
mScanQrImageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mInteractLiveTipsViewListener != null) {
mInteractLiveTipsViewListener.onQrClick();
}
}
});
}
public void setViewType(ViewType viewType) {
mViewType = viewType;
if (viewType == ViewType.INTERACTIVE) {
mInputUserIdEditText.setVisibility(View.VISIBLE);
mClearUserIdImageView.setVisibility(View.VISIBLE);
mInputRoomIdEditText.setVisibility(View.GONE);
mClearRoomIdImageView.setVisibility(View.GONE);
mInputUrlEditText.setVisibility(View.GONE);
mScanQrImageView.setVisibility(View.GONE);
mTipsTextView.setVisibility(View.VISIBLE);
mAudioRadioGroupLayout.setVisibility(View.VISIBLE);
mVideoRadioGroupLayout.setVisibility(View.VISIBLE);
} else if (viewType == ViewType.PK) {
mInputUserIdEditText.setVisibility(View.VISIBLE);
mClearUserIdImageView.setVisibility(View.VISIBLE);
mInputRoomIdEditText.setVisibility(View.VISIBLE);
mClearRoomIdImageView.setVisibility(View.VISIBLE);
mInputUrlEditText.setVisibility(View.GONE);
mScanQrImageView.setVisibility(View.GONE);
mTipsTextView.setVisibility(View.VISIBLE);
mAudioRadioGroupLayout.setVisibility(View.VISIBLE);
mVideoRadioGroupLayout.setVisibility(View.VISIBLE);
} else if (viewType == ViewType.BARE_STREAM) {
mInputUserIdEditText.setVisibility(View.GONE);
mInputRoomIdEditText.setVisibility(View.GONE);
mClearUserIdImageView.setVisibility(View.GONE);
mClearRoomIdImageView.setVisibility(View.GONE);
mInputUrlEditText.setVisibility(View.VISIBLE);
mScanQrImageView.setVisibility(View.VISIBLE);
mTipsTextView.setVisibility(View.VISIBLE);
mAudioRadioGroupLayout.setVisibility(View.GONE);
mVideoRadioGroupLayout.setVisibility(View.GONE);
}
}
public void setDefaultRoomId(String roomId) {
if (!TextUtils.isEmpty(roomId)) {
mInputRoomIdEditText.setText(roomId);
}
}
public void showInputView(String content, boolean showInputView) {
this.mShowInputView = showInputView;
if (!mShowInputView) {
mInputUserIdEditText.setVisibility(View.GONE);
mInputRoomIdEditText.setVisibility(View.GONE);
mInputUrlEditText.setVisibility(View.GONE);
mClearUserIdImageView.setVisibility(View.GONE);
mClearRoomIdImageView.setVisibility(View.GONE);
mTipsTextView.setVisibility(View.VISIBLE);
mTipsTextView.setText(content);
mAudioRadioGroupLayout.setVisibility(View.GONE);
mVideoRadioGroupLayout.setVisibility(View.GONE);
} else {
setViewType(mViewType);
}
}
public void setQrResult(String content) {
mInputUrlEditText.setText(content);
}
public void setOnInteractLiveTipsViewListener(InteractLiveTipsViewListener listener) {
this.mInteractLiveTipsViewListener = listener;
}
}

View File

@@ -0,0 +1,108 @@
package com.alivc.live.interactive_common.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.alivc.live.interactive_common.R;
/**
* 连麦 View
*/
public class InteractiveConnectView extends LinearLayout {
private FrameLayout mConnectFrameLayout;
private TextView mConnectTextView;
private OnConnectClickListener mOnConnectClickListener;
public InteractiveConnectView(@NonNull Context context) {
super(context);
init(context);
}
public InteractiveConnectView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public InteractiveConnectView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
setOrientation(LinearLayout.VERTICAL);
View inflate = LayoutInflater.from(context).inflate(R.layout.layout_interactive_connect_view, this, true);
mConnectFrameLayout = inflate.findViewById(R.id.fl_un_connect);
mConnectTextView = inflate.findViewById(R.id.tv_connect);
initListener();
}
private void initListener() {
mConnectTextView.setOnClickListener(view -> {
if (mOnConnectClickListener != null) {
mOnConnectClickListener.onConnectClick();
}
});
}
public void isShow(boolean isShowSurfaceView) {
mConnectFrameLayout.setVisibility(isShowSurfaceView ? View.INVISIBLE : View.VISIBLE);
}
public void setDefaultText(String text) {
mConnectTextView.setText(text);
}
/**
* 开始连接
*/
public void connected() {
connected(getResources().getString(R.string.interact_stop_connect));
}
/**
* 断开连接
*/
public void unConnected() {
unConnected(getResources().getString(R.string.interact_start_connect));
}
public void connected(String text) {
mConnectTextView.setText(text);
mConnectTextView.setBackground(getResources().getDrawable(R.drawable.shape_interact_live_un_connect_btn_bg));
}
public void unConnected(String text) {
mConnectTextView.setText(text);
mConnectTextView.setBackground(getResources().getDrawable(R.drawable.shape_pysh_btn_bg));
}
public FrameLayout getConnectFrameLayout() {
return mConnectFrameLayout;
}
public TextView getConnectTextView() {
return mConnectTextView;
}
public void setConnectClickListener(OnConnectClickListener listener) {
this.mOnConnectClickListener = listener;
}
/**
* 连麦按钮点击监听
*/
public interface OnConnectClickListener {
void onConnectClick();
}
}

View File

@@ -0,0 +1,116 @@
package com.alivc.live.interactive_common.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import com.alivc.live.interactive_common.R;
/**
* @author keria
* @date 2024/5/28
* @brief 直播连麦窗格控制操作栏
*/
public class InteractivePaneControlView extends LinearLayout {
private ImageView mInteractMuteImageView;
private ImageView mInteractSetEmptyView;
private ImageView mInteractResizeView;
private OnClickEventListener mOnClickEventListener;
private boolean mMuteAudio = false;
private boolean mEmptyView = false;
private boolean mResize = false;
public InteractivePaneControlView(Context context) {
super(context);
init(context);
}
public InteractivePaneControlView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public InteractivePaneControlView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.layout_interactive_pane_control, this, true);
mInteractMuteImageView = findViewById(R.id.iv_mute_audio);
mInteractSetEmptyView = findViewById(R.id.iv_show_view);
mInteractResizeView = findViewById(R.id.iv_view_resize);
setupListeners();
}
private void setupListeners() {
mInteractMuteImageView.setOnClickListener(v -> toggleState(
() -> mMuteAudio = !mMuteAudio,
() -> mInteractMuteImageView.setImageResource(mMuteAudio ? R.drawable.ic_interact_mute : R.drawable.ic_interact_unmute),
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickMuteAudio(mMuteAudio) : null
));
mInteractSetEmptyView.setOnClickListener(v -> toggleState(
() -> mEmptyView = !mEmptyView,
() -> mInteractSetEmptyView.setImageResource(mEmptyView ? R.drawable.ic_view_container_hide : R.drawable.ic_view_container_show),
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickEmptyView(mEmptyView) : null
));
mInteractResizeView.setOnClickListener(v -> toggleState(
() -> mResize = !mResize,
() -> mInteractResizeView.setImageResource(mResize ? R.drawable.ic_full_screen : R.drawable.ic_half_screen),
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickResize(mResize) : null
));
}
private void toggleState(@Nullable Runnable viewState, @Nullable Runnable viewStateToggler, @Nullable Runnable eventCallback) {
// 执行状态转换
if (viewState != null) {
viewState.run();
}
// 更新视图状态
if (viewStateToggler != null) {
viewStateToggler.run();
}
// 执行事件回调
if (eventCallback != null) {
eventCallback.run();
}
}
public void enableMute(boolean mute) {
if (mInteractMuteImageView != null) {
mInteractMuteImageView.setVisibility(mute ? View.VISIBLE : View.GONE);
}
}
public void setOnClickEventListener(OnClickEventListener listener) {
this.mOnClickEventListener = listener;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mOnClickEventListener = null;
}
public interface OnClickEventListener {
void onClickMuteAudio(boolean mute);
void onClickEmptyView(boolean empty);
void onClickResize(boolean resize);
}
}

View File

@@ -0,0 +1,83 @@
package com.alivc.live.interactive_common.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.alivc.live.interactive_common.R;
public class InteractiveQrEditView extends ConstraintLayout {
private TextView titleView;
private EditText etContentView;
private ImageView qrImageView;
private OnQrClickListener mQrClickListener;
public InteractiveQrEditView(@NonNull Context context) {
super(context);
init(context, null);
}
public InteractiveQrEditView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public InteractiveQrEditView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
View inflateView = LayoutInflater.from(context).inflate(R.layout.layout_interactive_qr_input_view, this, true);
titleView = inflateView.findViewById(R.id.tv_interactive_pull_url);
etContentView = inflateView.findViewById(R.id.et_interactive_pull_url);
qrImageView = inflateView.findViewById(R.id.iv_qr_pull_url);
if (attrs != null) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.InteractiveQrEditView);
String title = typedArray.getString(R.styleable.InteractiveQrEditView_title);
typedArray.recycle();
titleView.setText(title);
}
initListener();
}
private void initListener() {
qrImageView.setOnClickListener(view -> {
if (mQrClickListener != null) {
mQrClickListener.onQrClick();
}
});
}
public void setTitle(String title) {
titleView.setText(title);
}
public void setEditText(String text) {
etContentView.setText(text);
}
public String getEditText() {
return etContentView.getText().toString();
}
public void setOnQrClickListener(OnQrClickListener listener) {
this.mQrClickListener = listener;
}
public interface OnQrClickListener {
void onQrClick();
}
}

View File

@@ -0,0 +1,134 @@
package com.alivc.live.interactive_common.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import com.alivc.live.interactive_common.R;
/**
* @Author keria
* @Date 2024/5/28
* @Brief 直播连麦房间控制操作栏
*/
public class InteractiveRoomControlView extends LinearLayout {
private ImageView mSwitchCameraIv;
private ImageView mUseSpeakerPhoneIv;
private ImageView mMuteAudioIv;
private ImageView mMuteVideoIv;
private ImageView mEnableAudioCaptureIv;
private ImageView mEnableVideoCaptureIv;
private boolean mUseSpeakerPhone = false;
private boolean mMuteAudio = false;
private boolean mMuteVideo = false;
private boolean mEnableAudioCapture = true;
private boolean mEnableVideoCapture = true;
private OnClickEventListener mOnClickEventListener;
public InteractiveRoomControlView(Context context) {
super(context);
init(context);
}
public InteractiveRoomControlView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public InteractiveRoomControlView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.layout_interactive_room_control, this, true);
mSwitchCameraIv = findViewById(R.id.iv_camera);
mUseSpeakerPhoneIv = findViewById(R.id.iv_speaker_phone);
mMuteAudioIv = findViewById(R.id.iv_mute_audio);
mMuteVideoIv = findViewById(R.id.iv_mute_video);
mEnableAudioCaptureIv = findViewById(R.id.iv_enable_audio);
mEnableVideoCaptureIv = findViewById(R.id.iv_enable_video);
setupListeners();
}
private void setupListeners() {
mSwitchCameraIv.setOnClickListener(v -> toggleState(
null,
null,
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickSwitchCamera() : null
));
mUseSpeakerPhoneIv.setOnClickListener(v -> toggleState(
() -> mUseSpeakerPhone = !mUseSpeakerPhone,
() -> mUseSpeakerPhoneIv.setImageResource(mUseSpeakerPhone ? R.drawable.ic_speaker_phone_on : R.drawable.ic_speaker_phone_off),
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickSpeakerPhone(mUseSpeakerPhone) : null
));
mMuteAudioIv.setOnClickListener(v -> toggleState(
() -> mMuteAudio = !mMuteAudio,
() -> mMuteAudioIv.setImageResource(mMuteAudio ? R.drawable.ic_audio_capture_off : R.drawable.ic_audio_capture_on),
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickMuteAudio(mMuteAudio) : null
));
mMuteVideoIv.setOnClickListener(v -> toggleState(
() -> mMuteVideo = !mMuteVideo,
() -> mMuteVideoIv.setImageResource(mMuteVideo ? R.drawable.ic_local_video_off : R.drawable.ic_local_video_on),
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickMuteVideo(mMuteVideo) : null
));
mEnableAudioCaptureIv.setOnClickListener(v -> toggleState(
() -> mEnableAudioCapture = !mEnableAudioCapture,
() -> mEnableAudioCaptureIv.setImageResource(mEnableAudioCapture ? R.drawable.ic_audio_capture_on : R.drawable.ic_audio_capture_off),
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickEnableAudio(mEnableAudioCapture) : null
));
mEnableVideoCaptureIv.setOnClickListener(v -> toggleState(
() -> mEnableVideoCapture = !mEnableVideoCapture,
() -> mEnableVideoCaptureIv.setImageResource(mEnableVideoCapture ? R.drawable.ic_local_video_on : R.drawable.ic_local_video_off),
mOnClickEventListener != null ? () -> mOnClickEventListener.onClickEnableVideo(mEnableVideoCapture) : null
));
}
private void toggleState(@Nullable Runnable viewState, @Nullable Runnable viewStateToggler, @Nullable Runnable eventCallback) {
// 执行状态转换
if (viewState != null) {
viewState.run();
}
// 更新视图状态
if (viewStateToggler != null) {
viewStateToggler.run();
}
// 执行事件回调
if (eventCallback != null) {
eventCallback.run();
}
}
public void setOnClickEventListener(OnClickEventListener listener) {
this.mOnClickEventListener = listener;
}
public interface OnClickEventListener {
void onClickSwitchCamera();
void onClickSpeakerPhone(boolean enable);
void onClickMuteAudio(boolean mute);
void onClickMuteVideo(boolean mute);
void onClickEnableAudio(boolean enable);
void onClickEnableVideo(boolean enable);
}
}

View File

@@ -0,0 +1,41 @@
package com.alivc.live.interactive_common.widget;
import android.widget.FrameLayout;
import android.widget.TextView;
public class MultiAlivcLiveView {
private FrameLayout mUnConnectFrameLayout;
private FrameLayout mSmallFrameLayout;
private TextView mConnectTextView;
public MultiAlivcLiveView(FrameLayout unConnectFrameLayout,FrameLayout smallFrameLayout,TextView connectTextView){
this.mUnConnectFrameLayout = unConnectFrameLayout;
this.mSmallFrameLayout = smallFrameLayout;
this.mConnectTextView = connectTextView;
}
public FrameLayout getUnConnectFrameLayout() {
return mUnConnectFrameLayout;
}
public void setUnConnectFrameLayout(FrameLayout mUnConnectFrameLayout) {
this.mUnConnectFrameLayout = mUnConnectFrameLayout;
}
public FrameLayout getSmallFrameLayout() {
return mSmallFrameLayout;
}
public void setSmallFrameLayout(FrameLayout mSmallFrameLayout) {
this.mSmallFrameLayout = mSmallFrameLayout;
}
public TextView getConnectTextView() {
return mConnectTextView;
}
public void setConnectTextView(TextView mConnectTextView) {
this.mConnectTextView = mConnectTextView;
}
}

View File

@@ -0,0 +1,61 @@
package com.alivc.live.interactive_common.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.alivc.live.commonbiz.test.AliLiveStreamURLUtil;
import com.alivc.live.interactive_common.R;
import com.alivc.live.interactive_common.bean.InteractiveUserData;
import java.util.HashMap;
/**
* 用户连麦窗口显示'房间ID'和'用户ID'
*/
public class RoomAndUserInfoView extends LinearLayout {
private TextView mInfoTv;
public RoomAndUserInfoView(@NonNull Context context) {
super(context);
init(context);
}
public RoomAndUserInfoView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public RoomAndUserInfoView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
View inflateView = LayoutInflater.from(context).inflate(R.layout.layout_room_user_info_view, this, true);
mInfoTv = inflateView.findViewById(R.id.tv_info);
}
public void setUserInfo(String channelId, String userId) {
mInfoTv.setText(String.format("Room:%s\nUser:%s", channelId, userId));
}
public void setUrl(String url) {
HashMap<String, String> params = AliLiveStreamURLUtil.parseUrl(url);
mInfoTv.setText(String.format("Room:%s\nUser:%s",
AliLiveStreamURLUtil.parseURLStreamName(params),
params.get(AliLiveStreamURLUtil.USER_ID)));
}
public void setUserData(InteractiveUserData userData) {
mInfoTv.setText(String.format("Room:%s\nUser:%s",
userData != null ? userData.channelId : "",
userData != null ? userData.userId : ""));
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="16dp" />
<solid android:color="#23262F" />
</shape>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke android:width="1dp" android:color="#747A8C"/>
<corners android:radius="2dp" />
</shape>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#23262F" />
<corners android:radius="12dp" />
<stroke android:color="@color/colourful_border_weak" android:width="2dp" />
</shape>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="24dp"/>
<solid android:color="@color/shape_red_rectangle"/>
</shape>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#23262F" />
<corners android:radius="12dp" />
</shape>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="24dp"/>
<solid android:color="@color/colourful_ic_strong"/>
</shape>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#4D4DCFE1"/>
<stroke android:color="#4DCFE1" android:width="1px"/>
<corners android:radius="24dp" />
</shape>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 按钮的选择器,可以设置按钮在不同状态下的时候,按钮不同的颜色 -->
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_checked="true" android:drawable="@drawable/thumb_on" />
<item android:drawable="@drawable/thumb_off" />
</selector>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<size android:height="26dp" android:width="26dp"/>
<corners android:radius="13dp"/>
<solid android:color="#747A8C"/>
<stroke android:width="2dp"
android:color="#23262F"/>
</shape>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- 高度40 -->
<size
android:width="26dp"
android:height="26dp" />
<!-- 圆角弧度 20 -->
<corners android:radius="13dp" />
<solid android:color="#FCFCFD" />
<stroke android:width="2dp"
android:color="#4DCFE1"/>
</shape>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 底层下滑条的样式选择器可控制Switch在不同状态下底下下滑条的颜色 -->
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_checked="true" android:drawable="@drawable/track_on" />
<item android:drawable="@drawable/track_off" />
</selector>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<!-- 高度30 此处设置宽度无效-->
<size android:height="26dp"/>
<!-- 圆角弧度 15 -->
<corners android:radius="13dp"/>
<!-- 变化率 定义从左到右的颜色不变 -->
<solid android:color="#23262F"/>
</shape>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<size android:height="26dp"
android:width="50dp"/>
<corners android:radius="13dp" />
<solid android:height="26dp" android:color="#4DCFE1" />
</shape>

View File

@@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#1C1D22">
<ImageView
android:id="@+id/iv_back"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_marginStart="20dp"
android:src="@drawable/ic_live_action_bar_back"
app:layout_constraintBottom_toBottomOf="@id/tv_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_title" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:layout_marginTop="44dp"
android:gravity="center"
android:text="@string/app_info"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_app_id_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:layout_marginStart="20dp"
android:text="@string/app_info_app_id"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<EditText
android:id="@+id/et_app_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginEnd="20dp"
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_"
android:hint="@string/interact_live_user_id_hint"
android:maxLines="1"
android:singleLine="true"
android:textColor="#E6E7EC"
android:textColorHint="#E6E7EC"
android:theme="@style/AUIEditText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/tv_app_id_title"
app:layout_constraintTop_toBottomOf="@id/tv_app_id_title" />
<ImageView
android:id="@+id/iv_app_id_clear"
android:layout_width="18dp"
android:layout_height="18dp"
android:src="@drawable/ic_scan_icon"
app:layout_constraintBottom_toBottomOf="@id/et_app_id"
app:layout_constraintEnd_toEndOf="@id/et_app_id"
app:layout_constraintTop_toTopOf="@id/et_app_id" />
<TextView
android:id="@+id/tv_app_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="@string/app_info_app_key"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="@id/tv_app_id_title"
app:layout_constraintTop_toBottomOf="@id/et_app_id" />
<EditText
android:id="@+id/et_app_key"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_"
android:hint="@string/interact_live_user_id_hint"
android:maxLines="1"
android:singleLine="true"
android:textColor="#E6E7EC"
android:textColorHint="#E6E7EC"
android:theme="@style/AUIEditText"
app:layout_constraintEnd_toEndOf="@id/et_app_id"
app:layout_constraintStart_toStartOf="@id/tv_app_key"
app:layout_constraintTop_toBottomOf="@id/tv_app_key" />
<ImageView
android:id="@+id/iv_app_key_clear"
android:layout_width="18dp"
android:layout_height="18dp"
android:src="@drawable/ic_scan_icon"
app:layout_constraintBottom_toBottomOf="@id/et_app_key"
app:layout_constraintEnd_toEndOf="@id/et_app_key"
app:layout_constraintTop_toTopOf="@id/et_app_key" />
<TextView
android:id="@+id/tv_play_domain_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="@string/app_info_play_domain"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="@id/tv_app_id_title"
app:layout_constraintTop_toBottomOf="@id/et_app_key" />
<EditText
android:id="@+id/et_play_domain"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-_"
android:hint="@string/interact_live_user_id_hint"
android:maxLines="1"
android:singleLine="true"
android:textColor="#E6E7EC"
android:textColorHint="#E6E7EC"
android:theme="@style/AUIEditText"
app:layout_constraintEnd_toEndOf="@id/et_app_id"
app:layout_constraintStart_toStartOf="@id/tv_app_id_title"
app:layout_constraintTop_toBottomOf="@id/tv_play_domain_title" />
<ImageView
android:id="@+id/iv_play_domain_clear"
android:layout_width="18dp"
android:layout_height="18dp"
android:src="@drawable/ic_scan_icon"
app:layout_constraintBottom_toBottomOf="@id/et_play_domain"
app:layout_constraintEnd_toEndOf="@id/et_play_domain"
app:layout_constraintTop_toTopOf="@id/et_play_domain" />
<TextView
android:id="@+id/tv_confirm"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="44dp"
android:background="@drawable/shape_rect_blue"
android:ellipsize="end"
android:gravity="center"
android:includeFontPadding="false"
android:maxLines="2"
android:clickable="false"
android:text="@string/interact_live_next"
android:textColor="@android:color/white"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,235 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#1C1D22">
<ImageView
android:id="@+id/iv_back"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_marginStart="20dp"
android:src="@drawable/ic_live_action_bar_back"
app:layout_constraintBottom_toBottomOf="@id/tv_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_title" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:layout_marginTop="44dp"
android:gravity="center"
android:text="@string/interact_live"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_setting"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:layout_marginTop="44dp"
android:layout_marginEnd="10dp"
android:gravity="center"
android:text="@string/interact_setting"
android:textColor="#FCFCFD"
android:textSize="12sp"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="40dp"
app:layout_constraintBottom_toTopOf="@id/tv_confirm"
app:layout_constraintTop_toBottomOf="@id/tv_title">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_user_id_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:text="@string/interact_live_user_id"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/et_user_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginEnd="20dp"
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
android:hint="@string/interact_live_user_id_hint"
android:maxLength="20"
android:maxLines="1"
android:singleLine="true"
android:textColor="#E6E7EC"
android:textColorHint="#E6E7EC"
android:theme="@style/AUIEditText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/tv_user_id_title"
app:layout_constraintTop_toBottomOf="@id/tv_user_id_title" />
<ImageView
android:id="@+id/iv_user_id_clear"
android:layout_width="30dp"
android:layout_height="30dp"
android:padding="8dp"
android:src="@drawable/ic_close"
app:layout_constraintBottom_toBottomOf="@id/et_user_id"
app:layout_constraintEnd_toEndOf="@id/et_user_id"
app:layout_constraintTop_toTopOf="@id/et_user_id" />
<TextView
android:id="@+id/tv_room_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="@string/interact_live_room_id"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="@id/tv_user_id_title"
app:layout_constraintTop_toBottomOf="@id/et_user_id" />
<EditText
android:id="@+id/et_room_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
android:hint="@string/interact_live_user_id_hint"
android:maxLength="20"
android:maxLines="1"
android:singleLine="true"
android:textColor="#E6E7EC"
android:textColorHint="#E6E7EC"
android:theme="@style/AUIEditText"
app:layout_constraintEnd_toEndOf="@id/et_user_id"
app:layout_constraintStart_toStartOf="@id/tv_user_id_title"
app:layout_constraintTop_toBottomOf="@id/tv_room_number" />
<ImageView
android:id="@+id/iv_room_id_clear"
android:layout_width="30dp"
android:layout_height="30dp"
android:padding="8dp"
android:src="@drawable/ic_close"
app:layout_constraintBottom_toBottomOf="@id/et_room_id"
app:layout_constraintEnd_toEndOf="@id/et_room_id"
app:layout_constraintTop_toTopOf="@id/et_room_id" />
<fragment
android:id="@+id/fragment_app_info"
android:name="com.alivc.live.interactive_common.InteractLiveAppInfoFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/et_room_id" />
<LinearLayout
android:id="@+id/role_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="20dp"
android:orientation="horizontal"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/fragment_app_info"
tools:visibility="visible">
<com.alivc.live.interactive_common.widget.InteractLiveRadioButton
android:id="@+id/radio_button_anchor"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:ilrb_text="@string/interact_live_anchor" />
<View
android:layout_width="15dp"
android:layout_height="1dp" />
<com.alivc.live.interactive_common.widget.InteractLiveRadioButton
android:id="@+id/radio_button_audience"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
app:ilrb_text="@string/interact_live_audience" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:orientation="vertical"
android:padding="10dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/role_container">
<TextView
android:id="@+id/tv_url_entrance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00000000"
android:gravity="center"
android:includeFontPadding="false"
android:lineSpacingExtra="3dp"
android:padding="10dp"
android:singleLine="true"
android:text="@string/tips_use_url_for_interactive"
android:textColor="#FCFCFD"
android:textSize="16dp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_interactive_url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:includeFontPadding="false"
android:lineSpacingExtra="3dp"
android:padding="5dp"
android:textColor="#FCFCFD"
android:textSize="12dp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
<TextView
android:id="@+id/tv_confirm"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="44dp"
android:background="@drawable/shape_rect_blue"
android:clickable="false"
android:ellipsize="end"
android:gravity="center"
android:includeFontPadding="false"
android:maxLines="2"
android:text="@string/interact_live_confirm"
android:textColor="@android:color/white"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,299 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#1C1D22">
<ImageView
android:id="@+id/iv_back"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_marginStart="20dp"
android:src="@drawable/ic_live_action_bar_back"
app:layout_constraintBottom_toBottomOf="@id/tv_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_title" />
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="44dp"
android:layout_marginTop="44dp"
android:gravity="center"
android:text="@string/interact_setting"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/view_divider"
android:layout_width="match_parent"
android:layout_height="1px"
android:background="#3A3D48"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="20dp"
app:layout_constraintTop_toBottomOf="@id/view_divider"
app:layout_constraintBottom_toTopOf="@id/btn_commit">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="45dp">
<LinearLayout
android:id="@+id/tab_args_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tab_args_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="17dp"
android:layout_marginBottom="5dp"
android:text="@string/push_args"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_28px" />
<View
android:id="@+id/tab_args_view"
android:layout_width="32dp"
android:layout_height="3dp"
android:layout_gravity="bottom|center_horizontal"
android:background="#4DCFE1" />
</LinearLayout>
<LinearLayout
android:id="@+id/tab_action_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tab_action_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="17dp"
android:layout_marginBottom="5dp"
android:text="@string/stream_pusher_tip"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_28px" />
<View
android:id="@+id/tab_action_view"
android:layout_width="32dp"
android:layout_height="3dp"
android:layout_gravity="bottom|center_horizontal"
android:background="#4DCFE1"
android:visibility="invisible" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/multi_interactive_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/view_divider">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="10dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginTop="3dp"
android:layout_marginRight="10dp"
android:text="@string/pull_multi_interact_name_tv"
android:textColor="#FCFCFD"
android:textSize="14sp" />
<Switch
android:id="@+id/multi_interaction_control"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="16dp"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_alignParentBottom="true"
android:layout_marginTop="5dp"
android:background="#3A3D48" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="10dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginTop="3dp"
android:layout_marginRight="10dp"
android:text="@string/h5_compatible_name_tv"
android:textColor="#FCFCFD"
android:textSize="14sp" />
<Switch
android:id="@+id/h5_compatible_control"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="16dp"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_alignParentBottom="true"
android:layout_marginTop="5dp"
android:background="#3A3D48" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="10dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginTop="3dp"
android:layout_marginRight="10dp"
android:text="@string/data_channel_name_tv"
android:textColor="#FCFCFD"
android:textSize="14sp" />
<Switch
android:id="@+id/data_channel_control"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="16dp"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_alignParentBottom="true"
android:layout_marginTop="5dp"
android:background="#3A3D48" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="10dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginTop="3dp"
android:layout_marginRight="10dp"
android:text="@string/earback_open_without_headset"
android:textColor="#FCFCFD"
android:textSize="14sp" />
<Switch
android:id="@+id/earback_open_without_headset_control"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="16dp"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_alignParentBottom="true"
android:layout_marginTop="5dp"
android:background="#3A3D48" />
</RelativeLayout>
</LinearLayout>
<com.alivc.live.commonui.configview.LivePushSettingView
android:id="@+id/setting_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<Button
android:id="@+id/btn_commit"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="center"
android:layout_marginStart="20dp"
android:layout_marginTop="20dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="20dp"
android:background="@drawable/shape_pysh_btn_bg"
android:gravity="center"
android:orientation="horizontal"
android:text="@string/interact_live_confirm"
android:textColor="#FCFCFD"
android:textSize="18sp"
android:textStyle="normal"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#1C1D22">
<TextView
android:id="@+id/tv_app_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="39dp"
android:text="@string/app_info"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_arrow"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginEnd="12dp"
android:padding="8dp"
android:src="@drawable/ic_chevron_down"
app:layout_constraintBottom_toBottomOf="@id/tv_app_info"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_app_info" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_app_info"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="@id/iv_arrow"
app:layout_constraintStart_toStartOf="@id/tv_app_info"
app:layout_constraintTop_toBottomOf="@id/tv_app_info"
tools:visibility="visible">
<TextView
android:id="@+id/tv_app_id_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_info_app_id"
android:textColor="#B2B7C4"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_app_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#B2B7C4"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_app_id_title" />
<TextView
android:id="@+id/tv_app_key_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/app_info_app_key"
android:textColor="#B2B7C4"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_app_id_title" />
<TextView
android:id="@+id/tv_app_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#B2B7C4"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_app_key_title" />
<TextView
android:id="@+id/tv_play_domain_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/app_info_play_domain"
android:textColor="#B2B7C4"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_app_key_title" />
<TextView
android:id="@+id/tv_play_domain"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#B2B7C4"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_play_domain_title" />
<ImageView
android:id="@+id/iv_edit"
android:layout_width="16dp"
android:layout_height="36dp"
android:paddingVertical="10dp"
android:src="@drawable/ic_edit"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_play_domain" />
</androidx.constraintlayout.widget.ConstraintLayout>
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_marginHorizontal="20dp"
android:layout_marginTop="10dp"
android:background="#3A3D48"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cl_app_info" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="312dp"
android:layout_height="128dp"
android:background="@drawable/shape_interact_live_dialog_bg">
<TextView
android:id="@+id/tv_tips"
android:layout_width="wrap_content"
android:layout_height="70dp"
android:textColor="#FCFCFD"
android:textSize="16sp"
android:gravity="center"
android:text="@string/interact_live_lost_connect"
app:layout_constraintBottom_toBottomOf="@id/view_horizontal_line"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/view_horizontal_line"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#3A3D48"
app:layout_constraintTop_toBottomOf="@id/tv_tips"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/tv_confirm"
android:layout_width="0dp"
android:layout_height="48dp"
android:gravity="center"
android:text="@string/interact_live_confirm"
android:textColor="#4DCFE1"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/view_horizontal_line" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="120dp"
android:background="@drawable/shape_interact_live_unselected">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="12dp"
android:textColor="#FCFCFD"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:src="@drawable/ic_decorate"
app:layout_constraintEnd_toEndOf="@id/tv_title"
app:layout_constraintStart_toStartOf="@id/tv_title"
app:layout_constraintTop_toBottomOf="@id/tv_title" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:layout_marginBottom="15dp"
android:src="@drawable/ic_anchor"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="312dp"
android:layout_height="128dp"
android:background="@drawable/shape_interact_live_dialog_bg">
<TextView
android:id="@+id/tv_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/view_horizontal_line"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/et_input"
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:background="@drawable/shape_interact_live_edit_bg"
android:gravity="start|center_vertical"
android:hint="@string/interact_live_connect_tips"
android:maxLines="1"
android:paddingStart="10dp"
android:paddingEnd="0dp"
android:textColor="#E6E7EC"
android:textColorHint="#747A8C"
android:textSize="14sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/view_horizontal_line"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<ImageView
android:id="@+id/iv_clear"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginEnd="12dp"
android:src="@drawable/ic_close"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/et_input"
app:layout_constraintEnd_toEndOf="@id/et_input"
app:layout_constraintTop_toTopOf="@id/et_input"
tools:visibility="visible" />
<View
android:id="@+id/view_horizontal_line"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#3A3D48"
app:layout_constraintBottom_toTopOf="@id/tv_cancel"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="48dp"
android:gravity="center"
android:text="@string/interact_live_cancel"
android:textColor="#B2B7C4"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/view_vertical_line"
app:layout_constraintStart_toStartOf="parent" />
<View
android:id="@+id/view_vertical_line"
android:layout_width="1dp"
android:layout_height="48dp"
android:background="#3A3D48"
app:layout_constraintEnd_toStartOf="@id/tv_confirm"
app:layout_constraintStart_toEndOf="@id/tv_cancel"
app:layout_constraintTop_toTopOf="@id/tv_cancel" />
<TextView
android:id="@+id/tv_confirm"
android:layout_width="0dp"
android:layout_height="48dp"
android:gravity="center"
android:text="@string/interact_live_confirm"
android:textColor="#4DCFE1"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/view_vertical_line" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,258 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="312dp"
android:layout_height="wrap_content"
android:background="@drawable/shape_interact_live_dialog_bg">
<TextView
android:id="@+id/tv_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="24dp"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/view_horizontal_line"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/et_input_user_id"
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="24dp"
android:background="@drawable/shape_interact_live_edit_bg"
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
android:gravity="start|center_vertical"
android:hint="@string/interact_live_connect_author_tips"
android:maxLines="1"
android:paddingStart="10dp"
android:paddingEnd="0dp"
android:textColor="#E6E7EC"
android:textColorHint="#747A8C"
android:textSize="14sp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<ImageView
android:id="@+id/iv_clear_user_id"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginEnd="12dp"
android:src="@drawable/ic_close"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/et_input_user_id"
app:layout_constraintEnd_toEndOf="@id/et_input_user_id"
app:layout_constraintTop_toTopOf="@id/et_input_user_id" />
<EditText
android:id="@+id/et_input_room_id"
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="24dp"
android:background="@drawable/shape_interact_live_edit_bg"
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
android:gravity="start|center_vertical"
android:hint="@string/interact_live_connect_room_tips"
android:maxLines="1"
android:paddingStart="10dp"
android:paddingEnd="0dp"
android:textColor="#E6E7EC"
android:textColorHint="#747A8C"
android:textSize="14sp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/et_input_user_id"
tools:visibility="visible" />
<ImageView
android:id="@+id/iv_clear_room_id"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginEnd="12dp"
android:src="@drawable/ic_close"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/et_input_room_id"
app:layout_constraintEnd_toEndOf="@id/et_input_room_id"
app:layout_constraintTop_toTopOf="@id/et_input_room_id" />
<EditText
android:id="@+id/et_input_url"
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="24dp"
android:background="@drawable/shape_interact_live_edit_bg"
android:digits="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
android:gravity="start|center_vertical"
android:hint="@string/interact_live_url_connect_tips"
android:maxLines="1"
android:paddingStart="10dp"
android:paddingEnd="0dp"
android:textColor="#E6E7EC"
android:textColorHint="#747A8C"
android:textSize="14sp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/et_input_room_id"
tools:visibility="visible" />
<ImageView
android:id="@+id/iv_scan_qr"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginEnd="12dp"
android:src="@drawable/ic_scan_icon"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/et_input_url"
app:layout_constraintEnd_toEndOf="@id/et_input_url"
app:layout_constraintTop_toTopOf="@id/et_input_url" />
<LinearLayout
android:id="@+id/video_radio_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingHorizontal="10dp"
android:paddingVertical="5dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_input_url">
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:text="@string/interact_live_video_stream_type"
android:textColor="#FCFCFD" />
<RadioGroup
android:id="@+id/rg_video_stream_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton
android:id="@+id/rb_camera"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/interact_live_video_stream_type_camera"
android:textColor="#FCFCFD" />
<RadioButton
android:id="@+id/rb_screen"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:text="@string/interact_live_video_stream_type_screen"
android:textColor="#FCFCFD" />
</RadioGroup>
</LinearLayout>
<LinearLayout
android:id="@+id/audio_radio_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingHorizontal="10dp"
android:paddingVertical="5dp"
app:layout_constraintBottom_toBottomOf="@id/view_horizontal_line"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/video_radio_group">
<TextView
android:layout_width="100dp"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:text="@string/interact_live_audio_stream_type"
android:textColor="#FCFCFD" />
<RadioGroup
android:id="@+id/rg_audio_stream_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton
android:id="@+id/rb_mic"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/interact_live_audio_stream_type_mic"
android:textColor="#FCFCFD" />
<RadioButton
android:id="@+id/rb_dual"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:text="@string/interact_live_audio_stream_type_dual"
android:textColor="#FCFCFD" />
</RadioGroup>
</LinearLayout>
<View
android:id="@+id/view_horizontal_line"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#3A3D48"
app:layout_constraintBottom_toTopOf="@id/tv_cancel"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/tv_cancel"
android:layout_width="0dp"
android:layout_height="48dp"
android:gravity="center"
android:text="@string/interact_live_cancel"
android:textColor="#B2B7C4"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/view_vertical_line"
app:layout_constraintStart_toStartOf="parent" />
<View
android:id="@+id/view_vertical_line"
android:layout_width="1dp"
android:layout_height="48dp"
android:background="#3A3D48"
app:layout_constraintEnd_toStartOf="@id/tv_confirm"
app:layout_constraintStart_toEndOf="@id/tv_cancel"
app:layout_constraintTop_toTopOf="@id/tv_cancel" />
<TextView
android:id="@+id/tv_confirm"
android:layout_width="0dp"
android:layout_height="48dp"
android:gravity="center"
android:text="@string/interact_live_confirm"
android:textColor="#4DCFE1"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/view_vertical_line" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal">
<FrameLayout
android:id="@+id/fl_un_connect"
android:layout_width="95dp"
android:layout_height="160dp"
android:layout_marginBottom="15dp"
android:background="#23262F">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/ic_interact_live_un_connect_bg" />
</FrameLayout>
<TextView
android:id="@+id/tv_connect"
android:layout_width="94dp"
android:layout_height="30dp"
android:background="@drawable/shape_pysh_btn_bg"
android:ellipsize="end"
android:gravity="center"
android:includeFontPadding="false"
android:maxLines="1"
android:text="@string/interact_start_connect"
android:textColor="@android:color/white"
android:textSize="16sp" />
</merge>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_mute_audio"
android:layout_width="30dp"
android:layout_height="30dp"
android:padding="3dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_interact_unmute" />
<ImageView
android:id="@+id/iv_show_view"
android:layout_width="30dp"
android:layout_height="30dp"
android:padding="3dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_view_container_show" />
<ImageView
android:id="@+id/iv_view_resize"
android:layout_width="30dp"
android:layout_height="30dp"
android:padding="3dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_half_screen" />
</LinearLayout>

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_interactive_pull_url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/interact_live_pull_url"
android:textColor="#FCFCFD"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/et_interactive_pull_url"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="22dp"
android:layout_marginEnd="20dp"
android:maxLines="1"
android:singleLine="true"
android:textColor="#E6E7EC"
android:textColorHint="#E6E7EC"
android:theme="@style/AUIEditText"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/tv_interactive_pull_url"
app:layout_constraintTop_toBottomOf="@id/tv_interactive_pull_url" />
<ImageView
android:id="@+id/iv_qr_pull_url"
android:layout_width="30dp"
android:layout_height="30dp"
android:padding="8dp"
android:src="@drawable/ic_scan_icon"
app:layout_constraintBottom_toBottomOf="@id/et_interactive_pull_url"
app:layout_constraintEnd_toEndOf="@id/et_interactive_pull_url"
app:layout_constraintTop_toTopOf="@id/et_interactive_pull_url" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_camera"
android:layout_width="30dp"
android:layout_height="30dp"
android:padding="3dp"
android:src="@drawable/ic_camera" />
<ImageView
android:id="@+id/iv_speaker_phone"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginTop="10dp"
android:padding="3dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_speaker_phone_off" />
<ImageView
android:id="@+id/iv_mute_audio"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginTop="10dp"
android:padding="3dp"
android:src="@drawable/ic_audio_capture_on" />
<ImageView
android:id="@+id/iv_mute_video"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginTop="10dp"
android:padding="3dp"
android:src="@drawable/ic_local_video_on" />
<ImageView
android:id="@+id/iv_enable_audio"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginTop="10dp"
android:padding="3dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_audio_capture_on" />
<ImageView
android:id="@+id/iv_enable_video"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginTop="10dp"
android:padding="3dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_local_video_on" />
</LinearLayout>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FCFCFD"
android:textSize="15dp" />
</LinearLayout>

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pull_interact_name_tv">Connect Interact</string>
<string name="pull_interact_name_url">Connect Input URL</string>
<string name="pull_multi_interact_name_tv">Multi Connect Interact/PK</string>
<string name="h5_compatible_name_tv">H5 Compatible</string>
<string name="earback_open_without_headset">Earback Without Headset</string>
<string name="data_channel_name_tv">Data Channel</string>
<string name="external_audio_stream_tv">External Audio Stream</string>
<string name="external_video_stream_tv">External Video Stream</string>
<string name="dual_audio_stream">Push Dual Audio</string>
<string name="screen_share_stream">Push Screen Share</string>
<string name="change_resolution_and_fps">Change Resolution FPS</string>
<string name="force_ear_monitor">Force Ear Monitor</string>
<string name="push_interactive_setting_audio_only">Audio Only</string>
<string name="push_interactive_setting_gop">GOP</string>
<string name="push_interactive_setting_hardware_decode">Enable Hardware Decode</string>
<string name="push_interactive_h265">H265</string>
<string name="interact_live">Interact Live</string>
<string name="interact_live_url_title">Interact Live URL</string>
<string name="interact_live_push_url">Interact Live PUSH URL</string>
<string name="interact_live_pull_url">Interact Live PULL URL</string>
<string name="interact_start_push">Start Push</string>
<string name="interact_stop_push">Stop Push</string>
<string name="pk_live">PK Interaction</string>
<string name="interact_setting">Interact Setting</string>
<string name="interact_live_cancel">Cancel</string>
<string name="interact_live_confirm">Confirm</string>
<string name="interact_live_audio_stream_type">Audio Stream</string>
<string name="interact_live_audio_stream_type_mic">Mic</string>
<string name="interact_live_audio_stream_type_dual">Dual</string>
<string name="interact_live_video_stream_type">Video Stream</string>
<string name="interact_live_video_stream_type_camera">Camera</string>
<string name="interact_live_video_stream_type_screen">Screen</string>
<string name="interact_live_next">Next</string>
<string name="interact_live_user_id">User ID</string>
<string name="interact_live_room_id">Room ID</string>
<string name="app_info">APP Info</string>
<string name="app_info_app_id">App ID</string>
<string name="app_info_app_key">App Key</string>
<string name="app_info_play_domain">PlayDomain</string>
<string name="interact_live_user_id_hint">Please enter letters and numbers</string>
<string name="interact_live_anchor">anchor</string>
<string name="interact_live_audience">audience</string>
<string name="interact_input_limit">0/5</string>
<string name="interact_start_connect">Connect</string>
<string name="pk_start_connect">PK</string>
<string name="interact_stop_connect">UnConnect</string>
<string name="pk_stop_connect">EndPK</string>
<string name="interact_live_connecting">Connecting</string>
<string name="pk_live_connecting">Connecting</string>
<string name="interact_live_connect_tips">Enter the User ID</string>
<string name="interact_live_url_connect_tips">Enter the User URL</string>
<string name="interact_live_connect_author_tips">Enter the author ID</string>
<string name="interact_live_connect_room_tips">Enter the room ID</string>
<string name="interact_live_connect_input_error_tips">Input content</string>
<string name="interact_live_connect_finish_tips">End connect</string>
<string name="pk_live_connect_finish_tips">End PK</string>
<string name="interact_live_leave_room_tips">Leave room</string>
<string name="interact_live_viewer_left">Viewer Left</string>
<string name="interact_live_lost_connect">Disconnect the link, please re-enter the live room</string>
<string name="extern_stream_main">Main Extern Stream</string>
<string name="beauty_switch">Beauty</string>
<string name="waiting_download_video_resources">waiting for download external video resources</string>
<string name="interact_live_setting_resolution">Resolution</string>
<string name="target_bitrate">Target bitrate</string>
<string name="target_rate_value">1200</string>
<string name="captrue_fps">Captrue fps</string>
<string name="init_fps">20</string>
<string name="min_fps">Min fps</string>
<string name="live_push_switch_to_facing_back_camera">Switch to facing back camera,before scanning code</string>
<string name="tips_use_url_for_interactive">Use URL for interactive live</string>
<string name="tips_interactive_url_use_different_sdkappid">The sdkAppId used in the URL is different from the one in the App Info. Please note that this may cause pulling stream failure!</string>
</resources>

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pull_interact_name_tv">连麦互动</string>
<string name="pull_interact_name_url">推拉裸流</string>
<string name="pull_multi_interact_name_tv">多人连麦/PK</string>
<string name="h5_compatible_name_tv">H5兼容模式</string>
<string name="earback_open_without_headset">强制耳返</string>
<string name="data_channel_name_tv">DataChannel消息通道</string>
<string name="external_audio_stream_tv">自定义音频</string>
<string name="external_video_stream_tv">自定义视频</string>
<string name="dual_audio_stream">推音频辅流</string>
<string name="screen_share_stream">推屏幕共享流</string>
<string name="change_resolution_and_fps">切换分辨率与FPS</string>
<string name="force_ear_monitor">强制耳返</string>
<string name="push_interactive_setting_audio_only">纯音频</string>
<string name="push_interactive_setting_gop">GOP</string>
<string name="push_interactive_setting_hardware_decode">开启硬解</string>
<string name="push_interactive_h265">H265</string>
<string name="interact_live">连麦互动</string>
<string name="interact_live_url_title">推拉裸流配置</string>
<string name="interact_live_push_url">请输入推流地址</string>
<string name="interact_live_pull_url">请输入拉流地址</string>
<string name="interact_start_push">开始推流</string>
<string name="interact_stop_push">停止推流</string>
<string name="pk_live">PK互动</string>
<string name="interact_setting">参数设置</string>
<string name="interact_live_cancel">取消</string>
<string name="interact_live_confirm">确认</string>
<string name="interact_live_audio_stream_type">音频流类型</string>
<string name="interact_live_audio_stream_type_mic">麦克风流</string>
<string name="interact_live_audio_stream_type_dual">音频辅流</string>
<string name="interact_live_video_stream_type">视频流类型</string>
<string name="interact_live_video_stream_type_camera">摄像头流</string>
<string name="interact_live_video_stream_type_screen">屏幕共享</string>
<string name="interact_live_next">下一步</string>
<string name="interact_live_user_id">用户 ID</string>
<string name="interact_live_room_id">房间号</string>
<string name="app_info">应用信息</string>
<string name="app_info_app_id">App ID</string>
<string name="app_info_app_key">App Key</string>
<string name="app_info_play_domain">播流域名</string>
<string name="interact_live_user_id_hint">请输入字母、数字</string>
<string name="interact_live_anchor">主播</string>
<string name="interact_live_audience">观众</string>
<string name="interact_input_limit">0/5</string>
<string name="interact_start_connect">开始连麦</string>
<string name="pk_start_connect">开始PK</string>
<string name="interact_stop_connect">结束连麦</string>
<string name="pk_stop_connect">结束PK</string>
<string name="interact_live_connect_tips">请输入观众的用户ID</string>
<string name="interact_live_url_connect_tips">请输入连麦观众的 URL</string>
<string name="interact_live_connect_author_tips">请输入主播的用户ID</string>
<string name="interact_live_connect_room_tips">请输入主播的房间号</string>
<string name="interact_live_connect_input_error_tips">请输入内容</string>
<string name="interact_live_connecting">正在连麦</string>
<string name="pk_live_connecting">正在PK</string>
<string name="interact_live_connect_finish_tips">确认要结束本次连麦吗?</string>
<string name="pk_live_connect_finish_tips">确认要结束本次 PK 吗?</string>
<string name="interact_live_leave_room_tips">确认要退出房间吗?</string>
<string name="interact_live_viewer_left">观众已离开,结束连麦</string>
<string name="interact_live_lost_connect">断开链接,请重新进入直播间</string>
<string name="extern_stream_main">外部音视频</string>
<string name="beauty_switch">美颜</string>
<string name="waiting_download_video_resources">正在下载外部音视频资源中,请等待</string>
<string name="interact_live_setting_resolution">分辨率</string>
<string name="target_bitrate">视频目标码率</string>
<string name="target_rate_value">1200</string>
<string name="captrue_fps">采集帧率</string>
<string name="init_fps">20</string>
<string name="min_fps">最小帧率</string>
<string name="live_push_switch_to_facing_back_camera">扫码前请切换到后置摄像头</string>
<string name="tips_use_url_for_interactive">使用URL方式进行互动直播</string>
<string name="tips_interactive_url_use_different_sdkappid">URL中使用的sdkAppId与应用信息中的不同请注意这可能会导致拉流失败</string>
</resources>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="InteractLiveRadioButton">
<attr name="ilrb_text" format="string" />
</declare-styleable>
<declare-styleable name="InteractiveQrEditView">
<attr name="title" format="string" />
</declare-styleable>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="wheel_black">#000000</color>
<color name="transparent">#50000000</color>
<color name="alivc_color_black">#000</color>
<color name="shape_red_rectangle">#F53F3F</color>
</resources>

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pull_interact_name_tv">Connect Interact</string>
<string name="pull_interact_name_url">Connect Input URL</string>
<string name="pull_multi_interact_name_tv">Multi Connect Interact/PK</string>
<string name="h5_compatible_name_tv">H5 Compatible</string>
<string name="earback_open_without_headset">Earback Without Headset</string>
<string name="data_channel_name_tv">Data Channel</string>
<string name="external_audio_stream_tv">External Audio Stream</string>
<string name="external_video_stream_tv">External Video Stream</string>
<string name="dual_audio_stream">Push Dual Audio</string>
<string name="screen_share_stream">Push Screen Share</string>
<string name="change_resolution_and_fps">Change Resolution FPS</string>
<string name="force_ear_monitor">Force Ear Monitor</string>
<string name="push_interactive_setting_audio_only">Audio Only</string>
<string name="push_interactive_setting_gop">GOP</string>
<string name="push_interactive_setting_hardware_decode">Enable Hardware Decode</string>
<string name="push_interactive_h265">H265</string>
<string name="interact_live">Interact Live</string>
<string name="interact_live_url_title">Interact Live URL</string>
<string name="interact_live_push_url">Interact Live PUSH URL</string>
<string name="interact_live_pull_url">Interact Live PULL URL</string>
<string name="interact_start_push">Start Push</string>
<string name="interact_stop_push">Stop Push</string>
<string name="pk_live">PK Interaction</string>
<string name="interact_setting">Interact Setting</string>
<string name="interact_live_cancel">Cancel</string>
<string name="interact_live_confirm">Confirm</string>
<string name="interact_live_audio_stream_type">Audio Stream</string>
<string name="interact_live_audio_stream_type_mic">Mic</string>
<string name="interact_live_audio_stream_type_dual">Dual</string>
<string name="interact_live_video_stream_type">Video Stream</string>
<string name="interact_live_video_stream_type_camera">Camera</string>
<string name="interact_live_video_stream_type_screen">Screen</string>
<string name="interact_live_next">Next</string>
<string name="interact_live_user_id">User ID</string>
<string name="interact_live_room_id">Room ID</string>
<string name="app_info">APP Info</string>
<string name="app_info_app_id">App ID</string>
<string name="app_info_app_key">App Key</string>
<string name="app_info_play_domain">PlayDomain</string>
<string name="interact_live_user_id_hint">Please enter letters and numbers</string>
<string name="interact_live_anchor">anchor</string>
<string name="interact_live_audience">audience</string>
<string name="interact_input_limit">0/5</string>
<string name="interact_start_connect">Connect</string>
<string name="pk_start_connect">PK</string>
<string name="interact_stop_connect">UnConnect</string>
<string name="pk_stop_connect">EndPK</string>
<string name="interact_live_connecting">Connecting</string>
<string name="pk_live_connecting">Connecting</string>
<string name="interact_live_connect_tips">Enter the User ID</string>
<string name="interact_live_url_connect_tips">Enter the User URL</string>
<string name="interact_live_connect_author_tips">Enter the author ID</string>
<string name="interact_live_connect_room_tips">Enter the room ID</string>
<string name="interact_live_connect_input_error_tips">Input content</string>
<string name="interact_live_connect_finish_tips">End connect</string>
<string name="pk_live_connect_finish_tips">End PK</string>
<string name="interact_live_leave_room_tips">Leave room</string>
<string name="interact_live_viewer_left">Viewer Left</string>
<string name="interact_live_lost_connect">Disconnect the link, please re-enter the live room</string>
<string name="extern_stream_main">Main Extern Stream</string>
<string name="beauty_switch">Beauty</string>
<string name="waiting_download_video_resources">waiting for download external video resources</string>
<string name="interact_live_setting_resolution">Resolution</string>
<string name="target_bitrate">Target bitrate</string>
<string name="target_rate_value">1200</string>
<string name="captrue_fps">Captrue fps</string>
<string name="init_fps">20</string>
<string name="min_fps">Min fps</string>
<string name="live_push_switch_to_facing_back_camera">Switch to facing back camera,before scanning code</string>
<string name="tips_use_url_for_interactive">Use URL for interactive live</string>
<string name="tips_interactive_url_use_different_sdkappid">The sdkAppId used in the URL is different from the one in the App Info. Please note that this may cause pulling stream failure!</string>
</resources>

View File

@@ -0,0 +1,12 @@
<resources>
<style name="AUIEditText">
<item name="colorControlNormal">#3A3D48</item>
<item name="colorControlActivated">#3A3D48</item>
</style>
<style name="Push_SeekBar_Style"
parent="Widget.AppCompat.SeekBar">
<item name="colorAccent">#4DCFE1</item>
</style>
</resources>