集成完直播后提交代码

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

2
LiveCommon/live_commonbiz/.gitignore vendored Normal file
View File

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

View File

@@ -0,0 +1,75 @@
import java.text.SimpleDateFormat
plugins {
id 'com.android.library'
}
ext.MTL_buildId = getEnvValue("MUPP_BUILD_ID", 'localbuild')
ext.MTL_buildTimestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())
// special configurations, only used for test.
ext.Interactive_AppID = getEnvValue("INTERACTIVE_APP_ID", '')
ext.Interactive_AppKey = getEnvValue("INTERACTIVE_APP_KEY", '')
ext.Interactive_PlayDomain = getEnvValue("INTERACTIVE_PLAY_DOMAIN", '')
ext.isAllInOne = allInOne
String getEnvValue(key, defValue) {
def val = System.getProperty(key)
if (null != val) {
return val
}
val = System.getenv(key)
if (null != val) {
return val
}
return defValue
}
android {
compileSdkVersion androidCompileSdkVersion
buildToolsVersion androidBuildToolsVersion
defaultConfig {
minSdkVersion androidMinSdkVersion
targetSdkVersion androidTargetSdkVersion
versionCode 1
versionName "1.0"
consumerProguardFiles "consumer-rules.pro"
Properties properties = new Properties()
if (project.rootProject.file('local.properties').canRead()) {
properties.load(project.rootProject.file("local.properties").newDataInputStream())
}
resValue "string", "test_push_url", properties.getProperty("push.url", "")
resValue "string", "test_pull_url", properties.getProperty("pull.url", "")
resValue "string", "interactive_appid", properties.getProperty("interactive.appid", "")
resValue "string", "interactive_appkey", properties.getProperty("interactive.appkey", "")
resValue "string", "interactive_push_domain", properties.getProperty("interactive.push.domain", "")
resValue "string", "interactive_play_domain", properties.getProperty("interactive.play.domain", "")
}
buildTypes {
all {
buildConfigField "String", "MTL_BUILD_ID", "\"${MTL_buildId}\""
buildConfigField "String", "MTL_BUILD_TIMESTAMP", "\"${MTL_buildTimestamp}\""
buildConfigField "String", "INTERACTIVE_APP_ID", "\"${Interactive_AppID}\""
buildConfigField "String", "INTERACTIVE_APP_KEY", "\"${Interactive_AppKey}\""
buildConfigField "String", "INTERACTIVE_PLAY_DOMAIN", "\"${Interactive_PlayDomain}\""
buildConfigField "Boolean", "MTL_BUILD_FOR_AIO", "${isAllInOne}"
}
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
implementation 'commons-net:commons-net:3.8.0'
api project(':LiveCommon:live_commonui')
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.alivc.live.commonbiz">
<application>
<activity
android:name="com.alivc.live.commonbiz.backdoor.BackDoorActivity"
android:exported="true"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="backdoor"
android:scheme="livepush" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,216 @@
package com.alivc.live.commonbiz;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @note 该类为读取本地音视频流的实现类,用于外部音视频推流
* 注意为了适配本地不同格式的音视频流需要传入对应的profiles才能正常播放
* 如当前项目中capture0.yuv为720p请注意调节demo配置页中的分辨率为720p
*/
public class LocalStreamReader {
private static final String TAG = "LocalStreamReader";
private boolean videoThreadOn = false;
private boolean audioThreadOn = false;
private int videoWidth;
private int videoHeight;
private int videoStride;
private int videoSize;
private int videoRotation;
private int audioSampleRate;
private int audioChannel;
private int audioBufferSize;
public static final class Builder {
private final LocalStreamReader localStreamReader = new LocalStreamReader();
public Builder setVideoWith(int videoWith) {
localStreamReader.videoWidth = videoWith;
return this;
}
public Builder setVideoHeight(int videoHeight) {
localStreamReader.videoHeight = videoHeight;
return this;
}
public Builder setVideoSize(int videoSize) {
localStreamReader.videoSize = videoSize;
return this;
}
public Builder setVideoStride(int videoStride) {
localStreamReader.videoStride = videoStride;
return this;
}
public Builder setVideoRotation(int videoRotation) {
localStreamReader.videoRotation = videoRotation;
return this;
}
public Builder setAudioSampleRate(int audioSampleRate) {
localStreamReader.audioSampleRate = audioSampleRate;
return this;
}
public Builder setAudioChannel(int audioChannel) {
localStreamReader.audioChannel = audioChannel;
return this;
}
public Builder setAudioBufferSize(int audioBufferSize) {
localStreamReader.audioBufferSize = audioBufferSize;
return this;
}
public LocalStreamReader build() {
return localStreamReader;
}
}
public void readYUVData(File f, ReadYUVFileListener listener) {
new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
private AtomicInteger atoInteger = new AtomicInteger(0);
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("LivePushActivity-readYUV-Thread" + atoInteger.getAndIncrement());
return t;
}
}).execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
videoThreadOn = true;
InputStream myInput;
try {
myInput = new FileInputStream(f);
byte[] buffer = new byte[videoSize];
int length = myInput.read(buffer);
//发数据
while (length > 0 && videoThreadOn) {
long pts = System.currentTimeMillis() * 1000;
listener.onVideoStreamData(buffer, pts, videoWidth, videoHeight, videoStride, videoSize, videoRotation);
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
//发数据
length = myInput.read(buffer);
if (length <= 0) {
myInput.close();
myInput = new FileInputStream(f);
length = myInput.read(buffer);
}
}
myInput.close();
videoThreadOn = false;
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
public void readPCMData(File f, ReadPCMFileListener listener) {
new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
private AtomicInteger atoInteger = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("LivePushActivity-readPCM-Thread" + atoInteger.getAndIncrement());
return t;
}
}).execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
audioThreadOn = true;
int allSended = 0;
int sizePerSecond = audioSampleRate * 2;
InputStream myInput;
long startPts = System.nanoTime() / 1000;
try {
myInput = new FileInputStream(f);
byte[] buffer = new byte[audioBufferSize];
int length = myInput.read(buffer, 0, audioBufferSize);
if (audioSampleRate <= 0) {
Log.e(TAG, "audio sample rate error cause return: " + audioSampleRate);
return;
}
double sleep_time = 1000 / (audioSampleRate / 1000);
while (length > 0 && audioThreadOn) {
long start = System.nanoTime();
long pts = System.currentTimeMillis() * 1000;
listener.onAudioStreamData(buffer, length, pts, audioSampleRate, audioChannel);
allSended += length;
if ((allSended * 1000000L / sizePerSecond - 50000) > (pts - startPts)) {
try {
Thread.sleep(45);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
length = myInput.read(buffer);
if (length < audioBufferSize) {
myInput.close();
myInput = new FileInputStream(f);
length = myInput.read(buffer);
}
long end = System.nanoTime();
try {
long real_sleep_time = (long) (sleep_time - (end - start) / 1000 / 1000);
if (real_sleep_time > 0) {
Thread.sleep(real_sleep_time);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
myInput.close();
audioThreadOn = false;
} catch (IOException e) {
Log.e(TAG, "audio IOException: " + e.getLocalizedMessage());
e.printStackTrace();
}
}
});
}
public void stopYUV() {
videoThreadOn = false;
}
public void stopPcm() {
audioThreadOn = false;
}
public interface ReadYUVFileListener {
void onVideoStreamData(byte[] buffer, long pts, int videoWidth, int videoHeight, int videoStride, int videoSize, int videoRotation);
}
public interface ReadPCMFileListener {
void onAudioStreamData(byte[] buffer, int length, long pts, int audioSampleRate, int audioChannel);
}
}

View File

@@ -0,0 +1,26 @@
package com.alivc.live.commonbiz;
import android.content.Context;
import java.io.File;
public class ResourcesConst {
private ResourcesConst() {
}
/**
* capture0.yuv 文件本地保存路径
*/
public static File localCaptureYUVFilePath(Context context) {
return new File(context.getApplicationContext().getExternalFilesDir(null), "capture0.yuv");
}
/**
* 441.pcm 文件本地保存路径
*/
public static File localCapturePCMFilePath(Context context) {
return new File(context.getFilesDir().getPath() + File.separator + "alivc_resource/441.pcm");
}
}

View File

@@ -0,0 +1,138 @@
package com.alivc.live.commonbiz;
import android.app.DownloadManager;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import com.alivc.live.commonutils.DownloadUtil;
import com.alivc.live.commonutils.HttpUtils;
import com.alivc.live.commonutils.FileUtil;
import java.io.File;
import okhttp3.Response;
/**
* 远端资源下载管理类
*
* @note 用于下载存放在云端的yuv文件该yuv文件在外部音视频推流时使用
*/
public class ResourcesDownload {
private static final String TAG = "ResourcesDownload";
private static final String BASIC_YUV_OSS_URL = "https://alivc-demo-cms.alicdn.com/versionProduct/resources/livePush/capture0.yuv";
private static final String GLOBAL_YUV_OSS_URL = "https://alivc-demo-cms.alicdn.com/versionProduct/resources/livePush/capture0_SG.yuv";
private static final String FINAL_OSS_YUV_URL = BASIC_YUV_OSS_URL;
private static final Handler mHandler = new Handler(Looper.getMainLooper());
private ResourcesDownload() {
}
/**
* 下载yuv文件
*/
public static long downloadCaptureYUV(Context context, DownloadUtil.OnDownloadListener listener) {
DownloadUtil downloadUtil = new DownloadUtil(context.getApplicationContext());
File file = ResourcesConst.localCaptureYUVFilePath(context);
Log.d(TAG, "downloadYUV file : " + file.getAbsolutePath());
downloadUtil.setDownloadListener(new DownloadUtil.OnDownloadListener() {
@Override
public void onDownloadSuccess(long downloadId) {
Log.d(TAG, "onDownloadSuccess : " + downloadId);
verifyFile(downloadUtil, file, listener);
}
@Override
public void onDownloadProgress(long downloadId, double percent) {
Log.i(TAG, "onDownloadProgress : " + downloadId + ", " + percent);
if (listener != null) {
mHandler.post(() -> listener.onDownloadProgress(downloadId, percent));
}
}
@Override
public void onDownloadError(long downloadId, int errorCode, String errorMsg) {
Log.e(TAG, "onDownloadError : " + downloadId + ", " + errorCode + ", " + errorMsg);
if (listener != null) {
mHandler.post(() -> listener.onDownloadError(downloadId, errorCode, errorMsg));
}
FileUtil.safeDeleteFile(file);
}
});
if (file.exists()) {
//文件存在,验证完整性
verifyFile(downloadUtil, file, listener);
return -1;
} else {
return downloadUtil.startDownload(FINAL_OSS_YUV_URL, file);
}
}
/**
* 验证文件完整性
* 通过 Response 中 etag 字段获取 OSS 文件的 MD5
*/
private static void verifyFile(DownloadUtil downloadUtil, File file, DownloadUtil.OnDownloadListener listener) {
HttpUtils.get(FINAL_OSS_YUV_URL, new HttpUtils.OnNetWorkListener() {
@Override
public void onSuccess(Object obj) {
Response response = (Response) obj;
// 1、通过oss url获取etag和本地文件的md5进行比对是否一致
String responseMD5 = "";
try {
responseMD5 = response.header("etag").replace("\"", "");
} catch (Exception e) {
e.printStackTrace();
}
if (!TextUtils.isEmpty(responseMD5)) {
String fileMD5 = FileUtil.getFileMD5(file).trim();
if (responseMD5.equalsIgnoreCase(fileMD5)) {
if (listener != null) {
mHandler.post(() -> listener.onDownloadSuccess(-1));
}
return;
}
}
// 2、有时候etag是个非法值并不代表文件的实际md5那么直接对比文件的大小是否一致
long serverFileSize = -1;
try {
serverFileSize = Long.parseLong(response.header("Content-Length").trim());
} catch (Exception e) {
e.printStackTrace();
}
if (file != null && serverFileSize == file.length()) {
if (listener != null) {
mHandler.post(() -> listener.onDownloadSuccess(-1));
}
return;
}
FileUtil.safeDeleteFile(file);
downloadUtil.startDownload(FINAL_OSS_YUV_URL, file);
}
@Override
public void onFailure(int code, String msg) {
if (listener != null) {
mHandler.post(() -> listener.onDownloadError(-1, DownloadManager.ERROR_HTTP_DATA_ERROR, "code=" + code + ",msg=" + msg));
}
}
@Override
public void onError() {
if (listener != null) {
mHandler.post(() -> listener.onDownloadError(-1, DownloadManager.ERROR_HTTP_DATA_ERROR, "NetWork error"));
}
}
});
}
}

View File

@@ -0,0 +1,148 @@
package com.alivc.live.commonbiz;
import android.content.Context;
import androidx.annotation.NonNull;
import com.alivc.live.commonutils.SharedPrefUtils;
/**
* 推流Demo业务SharedPreference
*/
public class SharedPreferenceUtils {
private static final String AUTO_FOCUS = "autofocus";
private static final String PREVIEW_MIRROR = "previewmirror";
private static final String PUSH_MIRROR = "pushmirror";
private static final String TARGET_BIT = "target_bit";
private static final String MIN_BIT = "min_bit";
private static final String SHOW_GUIDE = "guide";
private static final String BEAUTY_ON = "beautyon";
private static final String HINT_TARGET_BIT = "hint_target_bit";
private static final String HINT_MIN_BIT = "hint_min_bit";
private static final String DISPLAY_FIT = "display_fit";
private static final String APP_ID = "app_id";
private static final String APP_KEY = "app_key";
private static final String PLAY_DOMAIN = "play_domain";
private static final String BARE_STREAM = "bare_stream";
private static final String USE_MULTI_PK_16IN = "useMultiPK16IN";
private static final String FORCE_RTC_PRE_ENVIRONMENT = "force_rtc_pre_environment";
public static void setPreviewMirror(@NonNull Context context, boolean previewMirror) {
SharedPrefUtils.saveData(context, PREVIEW_MIRROR, previewMirror);
}
public static boolean isPreviewMirror(@NonNull Context context, boolean defaultValue) {
return SharedPrefUtils.getBooleanData(context, PREVIEW_MIRROR, defaultValue);
}
public static void setPushMirror(@NonNull Context context, boolean pushMirror) {
SharedPrefUtils.saveData(context, PUSH_MIRROR, pushMirror);
}
public static boolean isPushMirror(@NonNull Context context, boolean defaultValue) {
return SharedPrefUtils.getBooleanData(context, PUSH_MIRROR, defaultValue);
}
public static void setAutofocus(@NonNull Context context, boolean autofocus) {
SharedPrefUtils.saveData(context, AUTO_FOCUS, autofocus);
}
public static boolean isAutoFocus(@NonNull Context context, boolean defaultValue) {
return SharedPrefUtils.getBooleanData(context, AUTO_FOCUS, defaultValue);
}
public static void setTargetBit(@NonNull Context context, int target) {
SharedPrefUtils.saveData(context, TARGET_BIT, target);
}
public static int getTargetBit(@NonNull Context context) {
return SharedPrefUtils.getIntData(context, TARGET_BIT, 0);
}
public static void setMinBit(@NonNull Context context, int min) {
SharedPrefUtils.saveData(context, MIN_BIT, min);
}
public static int getMinBit(@NonNull Context context) {
return SharedPrefUtils.getIntData(context, MIN_BIT, 0);
}
public static void setHintTargetBit(@NonNull Context context, int hintTarget) {
SharedPrefUtils.saveData(context, HINT_TARGET_BIT, hintTarget);
}
public static int getHintTargetBit(@NonNull Context context) {
return SharedPrefUtils.getIntData(context, HINT_TARGET_BIT, 0);
}
public static void setHintMinBit(@NonNull Context context, int hintMin) {
SharedPrefUtils.saveData(context, HINT_MIN_BIT, hintMin);
}
public static int getHintMinBit(@NonNull Context context) {
return SharedPrefUtils.getIntData(context, HINT_MIN_BIT, 0);
}
public static void setGuide(@NonNull Context context, boolean guide) {
SharedPrefUtils.saveData(context, SHOW_GUIDE, guide);
}
public static boolean isGuide(@NonNull Context context) {
return SharedPrefUtils.getBooleanData(context, SHOW_GUIDE, true);
}
public static void setBeautyOn(@NonNull Context context, boolean beautyOn) {
SharedPrefUtils.saveData(context, BEAUTY_ON, beautyOn);
}
public static boolean isBeautyOn(@NonNull Context context) {
return SharedPrefUtils.getBooleanData(context, BEAUTY_ON, true);
}
public static void setDisplayFit(@NonNull Context context, int displayfit) {
SharedPrefUtils.saveData(context, DISPLAY_FIT, displayfit);
}
public static int getDisplayFit(@NonNull Context context, int defaultValue) {
return SharedPrefUtils.getIntData(context, DISPLAY_FIT, defaultValue);
}
public static void setAppInfo(@NonNull Context context, String appId, String appKey, String playDomain) {
SharedPrefUtils.saveData(context, APP_ID, appId);
SharedPrefUtils.saveData(context, APP_KEY, appKey);
SharedPrefUtils.saveData(context, PLAY_DOMAIN, playDomain);
}
public static String getAppId(@NonNull Context context) {
return SharedPrefUtils.getStringData(context, APP_ID, "");
}
public static String getAppKey(@NonNull Context context) {
return SharedPrefUtils.getStringData(context, APP_KEY, "");
}
public static String getPlayDomain(@NonNull Context context) {
return SharedPrefUtils.getStringData(context, PLAY_DOMAIN, "");
}
public static void setMultiPK16IN(@NonNull Context context, boolean value) {
SharedPrefUtils.saveData(context, USE_MULTI_PK_16IN, value);
}
public static boolean getMultiPK16IN(@NonNull Context context) {
return SharedPrefUtils.getBooleanData(context, USE_MULTI_PK_16IN, false);
}
public static void setForceRTCPreEnvironment(@NonNull Context context, boolean value) {
SharedPrefUtils.saveData(context, FORCE_RTC_PRE_ENVIRONMENT, value);
}
public static boolean getForceRTCPreEnvironment(@NonNull Context context) {
return SharedPrefUtils.getBooleanData(context, FORCE_RTC_PRE_ENVIRONMENT, false);
}
public static void clear(@NonNull Context context) {
SharedPrefUtils.clear(context);
}
}

View File

@@ -0,0 +1,187 @@
package com.alivc.live.commonbiz.backdoor;
import android.content.ClipboardManager;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.alivc.live.commonbiz.R;
import com.alivc.live.commonbiz.SharedPreferenceUtils;
import com.alivc.live.commonui.utils.StatusBarUtil;
import com.alivc.live.commonui.widgets.LivePushTextSwitch;
import com.alivc.live.commonutils.AppUtil;
import com.alivc.live.commonutils.FileUtil;
import com.alivc.live.commonutils.ToastUtils;
import com.alivc.live.pusher.AlivcLiveBase;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
/**
* @author keria
* @date 2023/10/13
* @brief
*/
public class BackDoorActivity extends AppCompatActivity {
private static final String SD_CARD_LOG_SUFFIX = "device-info.log";
private TextView mInfoTv;
private LivePushTextSwitch mForceRTCPreEnvSw;
private LivePushTextSwitch mMultiPK16InSw;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
StatusBarUtil.translucent(this, Color.TRANSPARENT);
setContentView(R.layout.push_activity_backdoor);
initViews();
initData();
}
private void initViews() {
mForceRTCPreEnvSw = findViewById(R.id.sw_force_rtc_pre_env);
mForceRTCPreEnvSw.setTextViewText(getString(R.string.backdoor_rtc_force_pre_env));
mForceRTCPreEnvSw.setOnSwitchToggleListener(isChecked -> {
if (BackDoorInstance.getInstance().isForceRTCPreEnvironment() != isChecked) {
BackDoorInstance.getInstance().setForceRTCPreEnvironment(isChecked);
// 清除当前连麦配置信息
SharedPreferenceUtils.setAppInfo(getApplicationContext(), "", "", "");
// 退出APP
killApp();
}
});
mMultiPK16InSw = findViewById(R.id.sw_multi_pk_16in);
mMultiPK16InSw.setTextViewText(getString(R.string.backdoor_multi_pk_16in));
mMultiPK16InSw.setOnSwitchToggleListener(isChecked -> {
if (BackDoorInstance.getInstance().isUseMultiPK16IN() != isChecked) {
BackDoorInstance.getInstance().setUseMultiPK16IN(isChecked);
}
});
mInfoTv = findViewById(R.id.info_tv);
StringBuffer sb = new StringBuffer();
sb.append(getDemoBuildInfo());
sb.append("\n\n");
sb.append(getSdkBuildInfo());
sb.append("\n\n");
sb.append(AppUtil.getBriefDeviceInfo());
sb.append("\n\n");
sb.append(AppUtil.getDeviceInfo());
sb.append("\n\n");
sb.append(AppUtil.getFullCPUInfo());
sb.append("\n\n");
sb.append(AppUtil.getOpenGLESInfo(this));
sb.append("\n\n");
mInfoTv.setText(sb.toString());
mInfoTv.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
copyInfo2Clipboard();
dumpInfo2SdCard();
return true;
}
});
}
private void initData() {
mForceRTCPreEnvSw.setSwitchChecked(BackDoorInstance.getInstance().isForceRTCPreEnvironment());
mMultiPK16InSw.setSwitchChecked(BackDoorInstance.getInstance().isUseMultiPK16IN());
}
private static String getDemoBuildInfo() {
return String.format("%s%n%s%n%s%n%s",
"Demo Build Type: " + com.alivc.live.commonbiz.BuildConfig.BUILD_TYPE,
"Demo Build AIO: " + com.alivc.live.commonbiz.BuildConfig.MTL_BUILD_FOR_AIO,
"Demo Build ID: " + com.alivc.live.commonbiz.BuildConfig.MTL_BUILD_ID,
"Demo Build Timestamp: " + com.alivc.live.commonbiz.BuildConfig.MTL_BUILD_TIMESTAMP
);
}
private static String getSdkBuildInfo() {
return String.format("%s%n%s%n%s%n%s%n%s%n%s",
"SDK Version: " + AlivcLiveBase.getSDKVersion(),
"SDK Build Type: " + com.alivc.live.pusher.BuildConfig.BUILD_TYPE,
"SDK Build Interactive: " + com.alivc.live.pusher.BuildConfig.BUILD_INTERACTIVE,
"SDK Pre Environment: " + com.alivc.live.pusher.BuildConfig.PUSH_SDK_PRE_ENV,
"SDK Head Commit ID: " + com.alivc.live.pusher.BuildConfig.HEAD_COMMIT_ID,
"SDK Build ID: " + com.alivc.live.pusher.BuildConfig.MTL_BUILD_ID,
"SDK Build Timestamp: " + com.alivc.live.pusher.BuildConfig.MTL_BUILD_TIMESTAMP
);
}
private void copyInfo2Clipboard() {
ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
if (cm == null) {
return;
}
cm.setText(mInfoTv.getText());
ToastUtils.show("copy to clipboard success!");
}
private void dumpInfo2SdCard() {
removeOldSdCardLogs();
String folderPath = getSdCardLogPath();
String filename = String.format("%s-%s", new SimpleDateFormat("yyMMddHHmmss").format(new Date()), SD_CARD_LOG_SUFFIX);
String finalPath = FileUtil.combinePaths(folderPath, filename);
FileWriter fr = null;
try {
fr = new FileWriter(finalPath);
fr.write(mInfoTv.getText().toString());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void removeOldSdCardLogs() {
String folderPath = getSdCardLogPath();
ArrayList<File> files = FileUtil.listAllFiles(new File(folderPath), false);
if (files != null && !files.isEmpty()) {
for (File file : files) {
String path = file.getAbsolutePath();
if (path.endsWith(SD_CARD_LOG_SUFFIX)) {
FileUtil.safeDeleteFile(path);
}
}
}
}
private String getSdCardLogPath() {
String parentPath = getExternalFilesDir("").getAbsolutePath();
String folderPath = FileUtil.combinePaths(parentPath, "Ali_RTS_Log");
FileUtil.safeCreateFolder(folderPath);
return folderPath;
}
/**
* 强制退出APP
*/
private static void killApp() {
try {
Thread.sleep(2 * 1000L);
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,47 @@
package com.alivc.live.commonbiz.backdoor;
import com.alivc.live.commonbiz.SharedPreferenceUtils;
import com.alivc.live.commonutils.ContextUtils;
/**
* @author keria
* @date 2023/10/13
* @brief
*/
public class BackDoorInstance {
private boolean forceRTCPreEnvironment;
private boolean useMultiPK16IN;
private BackDoorInstance() {
forceRTCPreEnvironment = SharedPreferenceUtils.getForceRTCPreEnvironment(ContextUtils.getContext());
useMultiPK16IN = SharedPreferenceUtils.getMultiPK16IN(ContextUtils.getContext());
}
public static BackDoorInstance getInstance() {
return Inner.instance;
}
public boolean isForceRTCPreEnvironment() {
return this.forceRTCPreEnvironment;
}
public void setForceRTCPreEnvironment(boolean forceRTCPreEnvironment) {
this.forceRTCPreEnvironment = forceRTCPreEnvironment;
SharedPreferenceUtils.setForceRTCPreEnvironment(ContextUtils.getContext(), forceRTCPreEnvironment);
}
public boolean isUseMultiPK16IN() {
return this.useMultiPK16IN;
}
public void setUseMultiPK16IN(boolean useMultiPK16IN) {
this.useMultiPK16IN = useMultiPK16IN;
SharedPreferenceUtils.setMultiPK16IN(ContextUtils.getContext(), useMultiPK16IN);
}
private static class Inner {
private static final BackDoorInstance instance = new BackDoorInstance();
}
}

View File

@@ -0,0 +1,55 @@
package com.alivc.live.commonbiz.seidelay;
import com.alivc.live.commonbiz.seidelay.api.ISEIDelayEventListener;
import com.alivc.live.commonbiz.seidelay.handle.SEIDelayProvider;
import com.alivc.live.commonbiz.seidelay.handle.SEIDelayReceiver;
import com.alivc.live.commonbiz.seidelay.time.SEIDelayTimeHandler;
/**
* @author keria
* @date 2023/12/5
* @brief
*/
public class SEIDelayManager {
private SEIDelayProvider mProvider;
private SEIDelayReceiver mReceiver;
public SEIDelayManager() {
requestNTPTime();
}
public void registerProvider(String src, ISEIDelayEventListener listener) {
mProvider = new SEIDelayProvider();
mProvider.addListener(src, listener);
}
public void registerReceiver(ISEIDelayEventListener listener) {
mReceiver = new SEIDelayReceiver();
mReceiver.addListener("receiver", listener);
}
public void receiveSEI(SEISourceType sourceType, String sei) {
if (mReceiver != null) {
mReceiver.receiveSEI(sourceType, sei);
}
}
private void requestNTPTime() {
try {
new Thread(SEIDelayTimeHandler::requestNTPTime).start();
} catch (Exception e) {
e.printStackTrace();
}
}
public void destroy() {
if (mProvider != null) {
mProvider.destroy();
mProvider = null;
}
if (mReceiver != null) {
mReceiver.destroy();
mReceiver = null;
}
}
}

View File

@@ -0,0 +1,11 @@
package com.alivc.live.commonbiz.seidelay;
/**
* @author keria
* @date 2023/12/5
* @brief
*/
public enum SEISourceType {
CDN,
RTC
}

View File

@@ -0,0 +1,17 @@
package com.alivc.live.commonbiz.seidelay.api;
/**
* @author keria
* @date 2023/12/5
* @brief
*/
public interface ISEIDelayEventListener {
/**
* SEI delay event callback
*
* @param src SEI delay event source
* @param type SEI delay event type
* @param msg SEI delay event message
*/
void onEvent(String src, String type, String msg);
}

View File

@@ -0,0 +1,25 @@
package com.alivc.live.commonbiz.seidelay.api;
import java.util.HashMap;
import java.util.Map;
/**
* @author keria
* @date 2023/12/5
* @brief
*/
public abstract class ISEIDelayHandle {
public final Map<String, ISEIDelayEventListener> listenerHashMap = new HashMap<>();
public void addListener(String src, ISEIDelayEventListener listener) {
listenerHashMap.put(src, listener);
}
public void removeListener(String src) {
listenerHashMap.remove(src);
}
public void destroy() {
listenerHashMap.clear();
}
}

View File

@@ -0,0 +1,62 @@
package com.alivc.live.commonbiz.seidelay.data;
import org.json.JSONException;
import org.json.JSONObject;
/**
* @author keria
* @date 2023/12/5
* @brief
*/
public class SEIDelayProtocol {
private static final String SEI_DELAY = "sei_delay";
private static final String TIMESTAMP = "tm";
private static final String SRC = "src";
public String src = null;
public long tm = -1L;
public SEIDelayProtocol(String src, long tm) {
this.src = src;
this.tm = tm;
}
public SEIDelayProtocol(String json) {
try {
JSONObject jsonObject = new JSONObject(json);
String seiDelay = jsonObject.getString(SEI_DELAY);
JSONObject jsonObject2 = new JSONObject(seiDelay);
this.src = jsonObject2.getString(SRC);
this.tm = jsonObject2.getLong(TIMESTAMP);
} catch (JSONException e) {
}
}
private String generate() {
JSONObject jsonObject1 = new JSONObject();
try {
jsonObject1.put(SRC, this.src);
jsonObject1.put(TIMESTAMP, this.tm);
} catch (JSONException e) {
e.printStackTrace();
}
JSONObject jsonObject2 = new JSONObject();
try {
jsonObject2.put(SEI_DELAY, jsonObject1.toString());
} catch (JSONException e) {
e.printStackTrace();
}
return jsonObject2.toString();
}
@Override
public String toString() {
return generate();
}
}

View File

@@ -0,0 +1,88 @@
package com.alivc.live.commonbiz.seidelay.handle;
import com.alivc.live.commonbiz.seidelay.api.ISEIDelayEventListener;
import com.alivc.live.commonbiz.seidelay.api.ISEIDelayHandle;
import com.alivc.live.commonbiz.seidelay.data.SEIDelayProtocol;
import com.alivc.live.commonbiz.seidelay.time.SEIDelayTimeHandler;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Handles SEIDelay tasks and events.
* 对象 SEIDelayProvider 是一个处理 SEIDelay 任务和事件的类。
* 它使用一个计划好的任务执行器来定期运行任务。
* 当有监听器被添加或移除时,它会相应地启动或停止任务。
* 这个类主要处理 SEI 延迟相关的逻辑。
*
* @author keria
* @date 2023/12/5
* @brief
*/
public class SEIDelayProvider extends ISEIDelayHandle {
private static final long SCHEDULED_EXECUTOR_SERVICE_PERIOD = 2 * 1000L;
private static final int CORE_POOL_SIZE = 1;
private ScheduledThreadPoolExecutor mScheduledExecutorService;
private volatile boolean mTaskRun = false;
@Override
public void addListener(String src, ISEIDelayEventListener listener) {
super.addListener(src, listener);
if (!mTaskRun && !listenerHashMap.isEmpty()) {
mTaskRun = true;
startTask();
}
}
@Override
public void removeListener(String src) {
super.removeListener(src);
if (mTaskRun && listenerHashMap.isEmpty()) {
mTaskRun = false;
stopTask();
}
}
@Override
public void destroy() {
super.destroy();
stopTask();
}
private void startTask() {
stopTask();
mScheduledExecutorService = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(CORE_POOL_SIZE);
mScheduledExecutorService.scheduleAtFixedRate(this::provideSEI, 0, SCHEDULED_EXECUTOR_SERVICE_PERIOD, TimeUnit.MILLISECONDS);
}
private void stopTask() {
if (mScheduledExecutorService != null) {
mScheduledExecutorService.shutdown();
try {
if (!mScheduledExecutorService.awaitTermination(1, TimeUnit.SECONDS)) {
mScheduledExecutorService.shutdownNow();
}
} catch (InterruptedException e) {
mScheduledExecutorService.shutdownNow();
Thread.currentThread().interrupt();
e.printStackTrace();
} finally {
mScheduledExecutorService = null;
}
}
}
private void provideSEI() {
for (Map.Entry<String, ISEIDelayEventListener> entry : listenerHashMap.entrySet()) {
ISEIDelayEventListener eventListener = entry.getValue();
if (eventListener != null && SEIDelayTimeHandler.isNtpTimeUpdated()) {
long ntpTimestamp = SEIDelayTimeHandler.getCurrentTimestamp();
SEIDelayProtocol dataProtocol = new SEIDelayProtocol(entry.getKey(), ntpTimestamp);
eventListener.onEvent(dataProtocol.src, dataProtocol.src, dataProtocol.toString());
}
}
}
}

View File

@@ -0,0 +1,68 @@
package com.alivc.live.commonbiz.seidelay.handle;
import com.alivc.live.commonbiz.seidelay.SEISourceType;
import com.alivc.live.commonbiz.seidelay.api.ISEIDelayEventListener;
import com.alivc.live.commonbiz.seidelay.api.ISEIDelayHandle;
import com.alivc.live.commonbiz.seidelay.data.SEIDelayProtocol;
import com.alivc.live.commonbiz.seidelay.time.SEIDelayTimeHandler;
import java.util.Map;
/**
* SEIDelayReceiver handles the reception and processing of SEI data.
* It notifies listeners with the delay information.
*
* @author keria
* @date 2023/12/5
* @brief
*/
public class SEIDelayReceiver extends ISEIDelayHandle {
private static final long INVALID_THRESH_HOLD = 1000 * 60L;
@Override
public void destroy() {
super.destroy();
}
/**
* Receives SEI data and processes it to calculate delay and notify listeners.
*
* @param sourceType The type of SEI source.
* @param sei The SEI data.
*/
public void receiveSEI(SEISourceType sourceType, String sei) {
if (listenerHashMap.isEmpty()) {
return;
}
if (!SEIDelayTimeHandler.isNtpTimeUpdated()) {
return;
}
SEIDelayProtocol dataProtocol = new SEIDelayProtocol(sei);
long ntpTimestamp = SEIDelayTimeHandler.getCurrentTimestamp();
long delay = ntpTimestamp - dataProtocol.tm;
if (delay > INVALID_THRESH_HOLD) {
return;
}
notifyListeners(dataProtocol, sourceType, delay);
}
/**
* Notifies all registered listeners with the delay information.
*
* @param dataProtocol The SEIDelayProtocol containing SEI data.
* @param sourceType The type of SEI source.
* @param delay The calculated delay.
*/
private void notifyListeners(SEIDelayProtocol dataProtocol, SEISourceType sourceType, long delay) {
for (Map.Entry<String, ISEIDelayEventListener> entry : listenerHashMap.entrySet()) {
ISEIDelayEventListener eventListener = entry.getValue();
if (eventListener != null) {
eventListener.onEvent(dataProtocol.src, sourceType.toString(), String.valueOf(delay));
}
}
}
}

View File

@@ -0,0 +1,58 @@
package com.alivc.live.commonbiz.seidelay.time;
/**
* @author keria
* @date 2023/12/5
* @brief
*/
import android.util.Log;
import org.apache.commons.net.ntp.NTPUDPClient;
import org.apache.commons.net.ntp.TimeInfo;
import java.net.InetAddress;
public class SEIDelayTimeHandler {
private static final String TAG = "SEIDelayTimeHandler";
private static final String NTP_HOST = "ntp.aliyun.com";
private static long NTP_DELAY = 0L;
private static boolean NTP_TIME_UPDATED = false;
private SEIDelayTimeHandler() {
}
public static void requestNTPTime() {
NTPUDPClient client = new NTPUDPClient();
client.setDefaultTimeout(5000);
try {
InetAddress ntpServerAddress = InetAddress.getByName(NTP_HOST);
Log.i(TAG, "Connecting to NTP server: " + NTP_HOST);
TimeInfo timeInfo = client.getTime(ntpServerAddress);
timeInfo.computeDetails();
long returnTime = timeInfo.getMessage().getTransmitTimeStamp().getTime();
long systemTime = System.currentTimeMillis();
NTP_DELAY = returnTime - systemTime;
NTP_TIME_UPDATED = true;
Log.i(TAG, "Current time (from " + NTP_HOST + "): " + returnTime);
Log.i(TAG, "Current time (from system): " + systemTime);
Log.i(TAG, "Delay (NTP-system): " + NTP_DELAY + "ms");
} catch (Exception e) {
e.printStackTrace();
} finally {
client.close();
}
}
public static boolean isNtpTimeUpdated() {
return NTP_TIME_UPDATED;
}
public static long getCurrentTimestamp() {
return System.currentTimeMillis() + NTP_DELAY;
}
}

View File

@@ -0,0 +1,205 @@
package com.alivc.live.commonbiz.test;
import android.text.TextUtils;
import java.util.HashMap;
public class AliLiveStreamURLUtil {
/**
* 连麦推拉流地址的APPID/APPKEY/PLAY_DOMAIN
*/
public static final String APP_ID = PushDemoTestConstants.getTestInteractiveAppID();
public static final String APP_KEY = PushDemoTestConstants.getTestInteractiveAppKey();
public static final String PLAY_DOMAIN = PushDemoTestConstants.getTestInteractivePlayDomain();
/**
* 连麦推拉流地址的RTMP/HTTP-FLV/ARTC协议头
*/
private static final String RTMP = "rtmp://";
private static final String HTTP = "http://";
private static final String FLV = ".flv";
private static final String ARTC = "artc://";
/**
* DOMAIN 固定字段。
*/
public static final String ALILIVE_INTERACTIVE_DOMAIN = "live.aliyun.com";
/**
* 连麦推拉流地址的二级域名Second-level domain
*/
private static final String PULL_SLD = "play";
private static final String PUSH_SLD = "push";
/**
* 连麦推拉流地址的参数配置key
*/
public static final String BASIC_DOMAIN = "basicDomain";
public static final String SDK_APP_ID = "sdkAppId";
public static final String USER_ID = "userId";
public static final String TIMESTAMP = "timestamp";
public static final String TOKEN = "token";
private static final String APP_NAME = "live";
/**
* 旁路混流地址用,纯音频--->audio音视频--->camera
*/
private static final String STREAM_TASK_TYPE_CAMERA = "camera";
private static final String STREAM_TASK_TYPE_AUDIO = "audio";
private AliLiveStreamURLUtil() {
}
/**
* 根据 APPID/APPKEY/PLAY_DOMAIN 连麦配置信息,生成连麦推流地址
* <p>
* 需提前配置好以下连麦配置:
* {@link AliLiveUserSigGenerate#ALILIVE_APPID}
* {@link AliLiveUserSigGenerate#ALILIVE_APPKEY}
* {@link AliLiveUserSigGenerate#ALILIVE_PLAY_DOMAIN}
*
* @param channelId 频道 ID
* @param userId 用户 ID
* @return 连麦推流地址
*/
public static String generateInteractivePushUrl(String channelId, String userId) {
return generateInteractiveUrl(channelId, userId, true);
}
/**
* 根据 APPID/APPKEY/PLAY_DOMAIN 连麦配置信息,生成连麦拉流地址
* <p>
* 需提前配置好以下连麦配置:
* {@link AliLiveUserSigGenerate#ALILIVE_APPID}
* {@link AliLiveUserSigGenerate#ALILIVE_APPKEY}
* {@link AliLiveUserSigGenerate#ALILIVE_PLAY_DOMAIN}
*
* @param channelId 频道 ID
* @param userId 用户 ID
* @return 连麦实时拉流地址RTC用户实时互通用
*/
public static String generateInteractivePullUrl(String channelId, String userId) {
return generateInteractiveUrl(channelId, userId, false);
}
private static String generateInteractiveUrl(String channelId, String userId, boolean isPush) {
String sld = isPush ? PUSH_SLD : PULL_SLD;
long timestamp = AliLiveUserSigGenerate.getTimesTamp();
String token = AliLiveUserSigGenerate.createToken(APP_ID, APP_KEY, channelId, userId, timestamp);
return new StringBuilder(ARTC)
.append(ALILIVE_INTERACTIVE_DOMAIN).append("/")
.append(sld).append("/")
.append(channelId)
.append("?")
.append(SDK_APP_ID).append("=").append(APP_ID).append("&")
.append(USER_ID).append("=").append(userId).append("&")
.append(TIMESTAMP).append("=").append(timestamp).append("&")
.append(TOKEN).append("=").append(token)
.toString();
}
/**
* 根据 APPID/APPKEY/PLAY_DOMAIN 连麦配置信息生成普通观众的CDN拉流地址
* <p>
* 需提前配置好以下连麦配置:
* {@link AliLiveUserSigGenerate#ALILIVE_APPID}
* {@link AliLiveUserSigGenerate#ALILIVE_APPKEY}
* {@link AliLiveUserSigGenerate#ALILIVE_PLAY_DOMAIN}
*
* @param channelId 频道 ID
* @param userId 用户 ID
* @return 旁路直播拉流地址,普通观众用
*/
public static String generateCDNPullUrl(String channelId, String userId, boolean isAudioOnly) {
/**
* 建议将rtmp换成http-flv的形式。理由如下
* 在阿里云点播控制台生成地址时会同时生成RTMP与HTTP-FLV的地址这两个协议里包含的数据内容是一致的只是网络协议通道不一样。
* <p>
* HTTP协议是互联网主要协议CDN、运营商、中间网络设备等链路中都对HTTP有很长时间的网络优化
* HTTP的默认80/443端口号也是常见白名单端口不容易被禁用而RTMP协议比较老其默认端口号是1935有可能被防火墙等设备禁用导致异常。
* 因此在综合网络环境下HTTP-FLV的稳定性、性能卡顿、延时会比RTMP更好。
*/
return generateCDNFlvPullUrl(channelId, userId, isAudioOnly);
}
/**
* Parse url into auth info
*
* @param url url
* @return url params
*/
public static HashMap<String, String> parseUrl(String url) {
HashMap<String, String> map = new HashMap<>();
if (TextUtils.isEmpty(url)) {
return map;
}
String[] urlParts = url.trim().split("\\?");
map.put(BASIC_DOMAIN, urlParts[0]);
if (urlParts.length == 1) {
return map;
}
String[] params = urlParts[1].split("&");
for (String param : params) {
String[] keyValue = param.split("=");
if (keyValue.length < 2) {
continue;
}
map.put(keyValue[0], keyValue[1]);
}
return map;
}
/**
* Parse stream name from url params
*
* @param params url params
* @return stream name
*/
public static String parseURLStreamName(HashMap<String, String> params) {
if (params == null || !params.containsKey(BASIC_DOMAIN)) {
return "";
}
String domain = params.get(BASIC_DOMAIN);
if (TextUtils.isEmpty(domain)) {
return "";
}
String[] parts = domain.split("/");
return parts[parts.length - 1];
}
private static String generateCDNFlvPullUrl(String channelId, String userId, boolean isAudioOnly) {
String streamTaskType = isAudioOnly ? STREAM_TASK_TYPE_AUDIO : STREAM_TASK_TYPE_CAMERA;
return new StringBuilder(HTTP)
.append(PLAY_DOMAIN).append('/')
.append(APP_NAME).append('/')
.append(APP_ID).append('_')
.append(channelId).append('_')
.append(userId).append('_')
.append(streamTaskType)
.append(FLV)
.toString();
}
private static String generateCDNRtmpPullUrl(String channelId, String userId, boolean isAudioOnly) {
String streamTaskType = isAudioOnly ? STREAM_TASK_TYPE_AUDIO : STREAM_TASK_TYPE_CAMERA;
return new StringBuilder(RTMP)
.append(PLAY_DOMAIN).append('/')
.append(APP_NAME).append('/')
.append(APP_ID).append('_')
.append(channelId).append('_')
.append(userId).append('_')
.append(streamTaskType)
.toString();
}
}

View File

@@ -0,0 +1,85 @@
package com.alivc.live.commonbiz.test;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @note 备注该文件被直播连麦控制台的demo试用所链接请谨慎改动此文件的目录位置。
*/
public class AliLiveUserSigGenerate {
/**
* APP_ID在阿里云控制台应用管理页面创建和查看。
*/
public static final String ALILIVE_APPID = "PLACEHOLDER";
/**
* APP_KEY在阿里云控制台应用管理页面创建和查看。
*/
public static final String ALILIVE_APPKEY = "PLACEHOLDER";
/**
* CDN_DOMAIN在阿里云控制台应用管理页面创建和查看。
*/
public static final String ALILIVE_PLAY_DOMAIN = "PLACEHOLDER";
/**
* 过期时间
* 时间单位秒代表令牌有效时间。可设置最大范围是小于等于1天建议不要设置的过短或超过1天超过1天会不安全。
* 默认时间1天。1天 = 60 x 60 x 24。
*/
public static long getTimesTamp() {
return System.currentTimeMillis() / 1000 + 60 * 60 * 24;
}
/**
* 根据 appidappkeychannelIduserIdnonctimestamp 生层 token
*
* @param appid 应用ID。在控制台应用管理页面创建和查看。
* @param appkey 在控制台应用管理页面创建和查看。
* @param channelId 频道 ID
* @param userId 用户 ID
* @param timestamp 过期时间戳
* @return token
*/
public static String createToken(String appid, String appkey, String channelId, String userId, long timestamp) {
StringBuilder stringBuilder = new StringBuilder()
.append(appid)
.append(appkey)
.append(channelId)
.append(userId)
.append(timestamp);
return getSHA256(stringBuilder.toString());
}
/**
* 字符串签名
*
* @param str 输入源
* @return 返回签名
*/
public static String getSHA256(String str) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] hash = messageDigest.digest(str.getBytes(StandardCharsets.UTF_8));
return byte2Hex(hash);
} catch (NoSuchAlgorithmException e) {
// Consider logging the exception and/or re-throwing as a RuntimeException
e.printStackTrace();
}
return "";
}
private static String byte2Hex(byte[] bytes) {
StringBuilder stringBuilder = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
// Use single quote for char
stringBuilder.append('0');
}
stringBuilder.append(hex);
}
return stringBuilder.toString();
}
}

View File

@@ -0,0 +1,161 @@
package com.alivc.live.commonbiz.test;
import android.content.Context;
import android.text.TextUtils;
import com.alivc.live.commonbiz.BuildConfig;
import com.alivc.live.commonbiz.R;
import com.alivc.live.commonbiz.SharedPreferenceUtils;
import com.alivc.live.commonutils.ContextUtils;
/**
* Created by baorunchen on 2022/8/31.
* <p>
* Develop test data class. ONLY FOR TEST!!!!
* <p>
* First, we store dev test data into `local.properties` file, it's a gitignore file.
* <p>
* Such as:
* sdk.dir=/Users/keria/Library/Android/sdk
* push.url=rtmp://push-demo-rtmp.aliyunlive.com/test/stream/keriatest?&auth_key=xxxx
* pull.url=
* <p>
* And then, we can parse it from `build.gradle` file,
* if `local.properties` hasn't the property, it will use the default value.
*/
public class PushDemoTestConstants {
private static final String PLACEHOLDER = "PLACEHOLDER";
/**
* Get test push url, you won't need to scan url again!!!
* <p>
* put your test push url into `local.properties`
* such as: push.url=rtmp://xxx
*
* @return test push url
*/
public static String getTestPushUrl() {
Context context = ContextUtils.getContext();
return (context != null) ? context.getString(R.string.test_push_url) : "";
}
/**
* Get test pull url, you won't need to scan url again!!!
* <p>
* put your test pull url into `local.properties`
* such as: pull.url=rtmp://xxx
*
* @return test push url
*/
public static String getTestPullUrl() {
Context context = ContextUtils.getContext();
return (context != null) ? context.getString(R.string.test_pull_url) : "";
}
/**
* Get test interactive app id
* <p>
* put your app id into `local.properties`
* such as: interactive.appid=keriatest-appid
* <p>
* or, you can export it into system environment
* such as: export INTERACTIVE_APP_ID=keriatest-appid
*
* @return interactive app id
*/
public static String getTestInteractiveAppID() {
if (!checkIsPlaceholder(AliLiveUserSigGenerate.ALILIVE_APPID)) {
return AliLiveUserSigGenerate.ALILIVE_APPID;
}
if(!TextUtils.isEmpty(SharedPreferenceUtils.getAppId(ContextUtils.getApplicationContext()))){
return SharedPreferenceUtils.getAppId(ContextUtils.getApplicationContext());
}
if (!TextUtils.isEmpty(BuildConfig.INTERACTIVE_APP_ID)) {
return BuildConfig.INTERACTIVE_APP_ID;
}
Context context = ContextUtils.getContext();
if (context != null) {
return context.getString(R.string.interactive_appid);
}
return AliLiveUserSigGenerate.ALILIVE_APPID;
}
/**
* Get test interactive app key
* <p>
* put your app key into `local.properties`
* such as: interactive.appkey=keriatest-appkey
* <p>
* or, you can export it into system environment
* such as: export INTERACTIVE_APP_KEY=keriatest-appkey
*
* @return interactive app key
*/
public static String getTestInteractiveAppKey() {
if (!checkIsPlaceholder(AliLiveUserSigGenerate.ALILIVE_APPKEY)) {
return AliLiveUserSigGenerate.ALILIVE_APPKEY;
}
if (!TextUtils.isEmpty(SharedPreferenceUtils.getAppKey(ContextUtils.getApplicationContext()))) {
return SharedPreferenceUtils.getAppKey(ContextUtils.getApplicationContext());
}
if (!TextUtils.isEmpty(BuildConfig.INTERACTIVE_APP_KEY)) {
return BuildConfig.INTERACTIVE_APP_KEY;
}
Context context = ContextUtils.getContext();
if (context != null) {
return context.getString(R.string.interactive_appkey);
}
return AliLiveUserSigGenerate.ALILIVE_APPKEY;
}
/**
* Get test interactive play domain
* <p>
* put your app key into `local.properties`
* such as: interactive.playdomain=pullkeriatest.alivecdn.com
* <p>
* or, you can export it into system environment
* such as: export INTERACTIVE_PLAY_DOMAIN=pullkeriatest.alivecdn.com
*
* @return interactive play domain
*/
public static String getTestInteractivePlayDomain() {
if (!checkIsPlaceholder(AliLiveUserSigGenerate.ALILIVE_PLAY_DOMAIN)) {
return AliLiveUserSigGenerate.ALILIVE_PLAY_DOMAIN;
}
if (!TextUtils.isEmpty(SharedPreferenceUtils.getPlayDomain(ContextUtils.getApplicationContext()))) {
return SharedPreferenceUtils.getPlayDomain(ContextUtils.getApplicationContext());
}
if (!TextUtils.isEmpty(BuildConfig.INTERACTIVE_PLAY_DOMAIN)) {
return BuildConfig.INTERACTIVE_PLAY_DOMAIN;
}
Context context = ContextUtils.getContext();
if (context != null) {
return context.getString(R.string.interactive_play_domain);
}
return AliLiveUserSigGenerate.ALILIVE_PLAY_DOMAIN;
}
/**
* Check whether configuration value is placeholder or not
*
* @param configuration configuration value
* @return true, false
*/
public static boolean checkIsPlaceholder(String configuration) {
return TextUtils.isEmpty(configuration) || TextUtils.equals(configuration, PLACEHOLDER);
}
}

View File

@@ -0,0 +1,86 @@
package com.alivc.live.commonbiz.testapi;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.GLUtils;
import android.util.Log;
/**
* @author keria
* @date 2024/7/5
* @brief FIXME keria:测试代码
*/
public class EGLContextTest {
private static final String TAG = "EGLContextTest";
private EGLContextTest() {
}
/**
* 创建 EGLContext
*/
public static void testGLContext() {
int maxContexts = 0;
// 假设尝试创建最多1000个上下文
EGLContext[] contexts = new EGLContext[1000];
EGLDisplay eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (eglDisplay == EGL14.EGL_NO_DISPLAY) {
Log.e(TAG, "Unable to get EGL14 display");
return;
}
int[] version = new int[2];
if (!EGL14.eglInitialize(eglDisplay, version, 0, version, 1)) {
Log.e(TAG, "Unable to initialize EGL14");
return;
}
int[] configAttribs = {
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_RED_SIZE, 8,
EGL14.EGL_GREEN_SIZE, 8,
EGL14.EGL_BLUE_SIZE, 8,
EGL14.EGL_ALPHA_SIZE, 8,
EGL14.EGL_DEPTH_SIZE, 24,
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfigs = new int[1];
if (!EGL14.eglChooseConfig(eglDisplay, configAttribs, 0, configs, 0, 1, numConfigs, 0)) {
Log.e(TAG, "Unable to choose config");
return;
}
int[] contextAttribs = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL14.EGL_NONE
};
for (int i = 0; i < contexts.length; i++) {
contexts[i] = EGL14.eglCreateContext(eglDisplay, configs[0], EGL14.EGL_NO_CONTEXT, contextAttribs, 0);
if (contexts[i] == EGL14.EGL_NO_CONTEXT) {
Log.e(TAG, "Failed to create EGL context at index " + i + ": " + EGL14.eglGetError() + " - " + GLUtils.getEGLErrorString(EGL14.eglGetError()));
break;
}
maxContexts++;
}
Log.d(TAG, "Maximum number of OpenGL ES contexts supported: " + maxContexts);
// 清理所有创建的上下文
for (int i = 0; i < maxContexts; i++) {
if (contexts[i] != EGL14.EGL_NO_CONTEXT) {
EGL14.eglDestroyContext(eglDisplay, contexts[i]);
}
}
EGL14.eglTerminate(eglDisplay);
}
}

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="30dp"
android:gravity="center"
android:text="@string/backdoor_title"
android:textColor="#747A8C"
android:textSize="20dp" />
<TextView
android:id="@+id/tv_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="5dp"
android:gravity="center"
android:text="@string/backdoor_tip"
android:textColor="#B2B7C4"
android:textSize="11dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="10dp"
android:background="#3A3D48"
android:orientation="vertical"
android:padding="10dp">
<com.alivc.live.commonui.widgets.LivePushTextSwitch
android:id="@+id/sw_force_rtc_pre_env"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.alivc.live.commonui.widgets.LivePushTextSwitch
android:id="@+id/sw_multi_pk_16in"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="6dp"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="6dp">
<TextView
android:id="@+id/info_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center_vertical"
android:lineSpacingMultiplier="1.5"
android:padding="6dp"
android:textSize="12dp" />
</LinearLayout>
</ScrollView>
</LinearLayout>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="backdoor_title">Live Push Demo Configuration Page</string>
<string name="backdoor_tip">Used to manually configure demo corresponding functions</string>
<string name="backdoor_rtc_force_pre_env">Force RTC Pre Environment</string>
<string name="backdoor_multi_pk_16in">Multi PK 16IN</string>
</resources>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="backdoor_title">Live Push Demo配置页面</string>
<string name="backdoor_tip">用于手动配置demo相应功能</string>
<string name="backdoor_rtc_force_pre_env">强制RTC预发环境</string>
<string name="backdoor_multi_pk_16in">16方多人PK</string>
</resources>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="backdoor_title">Live Push Demo Configuration Page</string>
<string name="backdoor_tip">Used to manually configure demo corresponding functions</string>
<string name="backdoor_rtc_force_pre_env">Force RTC Pre Environment</string>
<string name="backdoor_multi_pk_16in">Multi PK 16IN</string>
</resources>

2
LiveCommon/live_commonui/.gitignore vendored Normal file
View File

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

View File

@@ -0,0 +1,41 @@
plugins {
id 'com.android.library'
}
android {
compileSdkVersion androidCompileSdkVersion
buildToolsVersion androidBuildToolsVersion
defaultConfig {
minSdkVersion androidMinSdkVersion
targetSdkVersion androidTargetSdkVersion
versionCode 1
versionName "1.0"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
viewBinding {
enabled = true
}
}
dependencies {
implementation externalAndroidDesign
api project(':LiveCommon:live_commonutils')
def isInteractive = project.hasProperty("sdk_type") && "AliVCSDK_InteractiveLive".equalsIgnoreCase(sdk_type)
api isInteractive ? externalLivePusherInteractive : externalLivePusher
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.alivc.live.commonui">
</manifest>

View File

@@ -0,0 +1,108 @@
package com.alivc.live.commonui.activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.alivc.live.commonui.R;
public class AUILiveActionBar extends RelativeLayout {
private ImageView mLeftImageView;
private ImageView mRightImageView;
private TextView mTitleView;
public AUILiveActionBar(Context context) {
this(context, null);
}
public AUILiveActionBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AUILiveActionBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
LayoutInflater.from(context).inflate(R.layout.aui_live_actionbar_layout, this, true);
mLeftImageView = findViewById(R.id.aui_player_actionbar_left_image);
mRightImageView = findViewById(R.id.aui_player_actionbar_right_image);
mTitleView = findViewById(R.id.aui_player_actionbar_title);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.av_actionbar);
if (ta == null) {
return;
}
boolean isShowTitle = ta.getBoolean(R.styleable.av_actionbar_showTitle, false);
mTitleView.setVisibility(isShowTitle ? VISIBLE : GONE);
String title = ta.getString(R.styleable.av_actionbar_title);
mTitleView.setText(title);
boolean isShowLeftImage = ta.getBoolean(R.styleable.av_actionbar_showLeftView, false);
mLeftImageView.setVisibility(isShowLeftImage ? VISIBLE : GONE);
int leftBackground = ta.getResourceId(R.styleable.av_actionbar_leftImageBackground, 0);
if (leftBackground != 0) {
mLeftImageView.setBackgroundResource(leftBackground);
}
int leftImage = ta.getResourceId(R.styleable.av_actionbar_leftImageSrc, 0);
if (leftImage != 0) {
mLeftImageView.setImageResource(leftImage);
}
boolean isShowRightImage = ta.getBoolean(R.styleable.av_actionbar_showRightView, false);
mRightImageView.setVisibility(isShowRightImage ? VISIBLE : GONE);
int rightBackground = ta.getResourceId(R.styleable.av_actionbar_rightImageBackground, 0);
if (rightBackground != 0) {
mRightImageView.setBackgroundResource(rightBackground);
}
int rightImage = ta.getResourceId(R.styleable.av_actionbar_rightImageSrc, 0);
if (rightImage != 0) {
mRightImageView.setImageResource(rightImage);
}
}
public void showLeftView() {
mLeftImageView.setVisibility(VISIBLE);
}
public void hideLeftView() {
mLeftImageView.setVisibility(GONE);
}
public void showRightView() {
mRightImageView.setVisibility(VISIBLE);
}
public void hideRightView() {
mRightImageView.setVisibility(GONE);
}
public void showTitleView() {
mTitleView.setVisibility(VISIBLE);
}
public void hideTitleView() {
mTitleView.setVisibility(GONE);
}
public ImageView getLeftImageView() {
return mLeftImageView;
}
public ImageView getRightImageView() {
return mRightImageView;
}
public TextView getTitleView() {
return mTitleView;
}
}

View File

@@ -0,0 +1,171 @@
package com.alivc.live.commonui.activity;
import android.graphics.Color;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.alivc.live.commonui.R;
import com.alivc.live.commonui.utils.StatusBarUtil;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
public abstract class AUILiveBaseListActivity extends AppCompatActivity {
private AUILiveActionBar mAUILiveActionBar;
private RecyclerView mRecyclerView;
private AVListAdapter mAVListAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
getWindow().setBackgroundDrawable(null);
getDelegate().setLocalNightMode(specifiedThemeMode());
super.onCreate(savedInstanceState);
StatusBarUtil.translucent(this, Color.TRANSPARENT);
setContentView(R.layout.aui_live_activity_list_layout);
initBaseView();
initBaseData();
}
private void initBaseView() {
mAUILiveActionBar = findViewById(R.id.aui_player_base_title);
mRecyclerView = findViewById(R.id.aui_player_base_main_recyclerView);
mAVListAdapter = new AVListAdapter(this);
mRecyclerView.setAdapter(mAVListAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mAUILiveActionBar.getLeftImageView().setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
onBackPressed();
}
});
}
private void initBaseData() {
mAUILiveActionBar.getTitleView().setText(getTitleResId());
if (showBackBtn()) {
mAUILiveActionBar.showLeftView();
} else {
mAUILiveActionBar.hideLeftView();
}
mAVListAdapter.setData(createListData());
}
protected int specifiedThemeMode() {
return AppCompatDelegate.MODE_NIGHT_NO;
}
/**
* 标题的resourceID 不要直接使用文本,要适配多语言
*
* @return
*/
public abstract int getTitleResId();
/**
* 是否显示返回按钮
*
* @return
*/
public abstract boolean showBackBtn();
public abstract List<ListModel> createListData();
public abstract void onListItemClick(ListModel model);
public static class ListModel {
public int index;
public int drawableResId;
public String title;
public String desc;
public ListModel(int index, int drawableResId, String title, String desc) {
this.index = index;
this.drawableResId = drawableResId;
this.title = title;
this.desc = desc;
}
}
private static class AVListAdapter extends RecyclerView.Adapter<AVListHolder> {
private List<ListModel> mData = new ArrayList<>();
private WeakReference<AUILiveBaseListActivity> mAVBaseListActivityRef;
public AVListAdapter(AUILiveBaseListActivity activity) {
mAVBaseListActivityRef = new WeakReference<>(activity);
}
public void setData(List<ListModel> data) {
mData.clear();
mData.addAll(data);
notifyDataSetChanged();
}
public long getItemId(int position) {
return position;
}
@NonNull
@Override
public AVListHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.aui_live_base_list_item, parent, false);
AVListHolder avListHolder = new AVListHolder(itemView);
avListHolder.mImageView = itemView.findViewById(R.id.av_list_item_image);
avListHolder.mTitle = itemView.findViewById(R.id.av_list_item_title);
avListHolder.mDesc = itemView.findViewById(R.id.av_list_item_desc);
return avListHolder;
}
@Override
public void onBindViewHolder(@NonNull AVListHolder holder, int position) {
ListModel listModel = mData.get(position);
if (listModel == null) {
return;
}
holder.mImageView.setImageResource(listModel.drawableResId);
holder.mTitle.setText(listModel.title);
if (!TextUtils.isEmpty(listModel.desc)) {
holder.mDesc.setText(listModel.desc);
holder.mDesc.setVisibility(View.VISIBLE);
} else {
holder.mDesc.setVisibility(View.GONE);
}
holder.itemView.setOnClickListener(view -> {
if (mAVBaseListActivityRef.get() != null) {
mAVBaseListActivityRef.get().onListItemClick(mData.get(position));
}
});
}
@Override
public int getItemCount() {
return mData.size();
}
}
private static class AVListHolder extends RecyclerView.ViewHolder {
ImageView mImageView;
TextView mTitle;
TextView mDesc;
public AVListHolder(@NonNull View itemView) {
super(itemView);
}
}
}

View File

@@ -0,0 +1,152 @@
package com.alivc.live.commonui.avdialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.TranslateAnimation;
import androidx.annotation.NonNull;
import com.alivc.live.commonui.R;
public class AUILiveDialog extends Dialog {
private final static int ANIMATION_DURATION = 200;
private View mContentView;
private boolean mIsAnimating = false;
public AUILiveDialog(@NonNull Context context) {
super(context, R.style.AUILiveDialog);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().getDecorView().setPadding(0, 0, 0, 0);
}
@Override
public void setContentView(@NonNull View view) {
mContentView = view;
super.setContentView(view);
}
public void setLayoutBySreenMode() {
WindowManager.LayoutParams params = getWindow().getAttributes();
params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
params.gravity = Gravity.CENTER;
// int screenWidth = DensityUtil.getDisplayMetrics(getContext()).widthPixels;
// int screenHeight = DensityUtil.getDisplayMetrics(getContext()).heightPixels;
// params.width = Math.min(screenWidth, screenHeight);
getWindow().setAttributes(params);
setCanceledOnTouchOutside(true);
}
/**
* ChoiceItemBottomDialog从下往上升起的动画动画
*/
private void animateUp() {
if (mContentView != null) {
return;
}
TranslateAnimation translateAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f,
Animation.RELATIVE_TO_SELF, 1f, Animation.RELATIVE_TO_SELF, 0f);
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);
AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(translateAnimation);
animationSet.addAnimation(alphaAnimation);
animationSet.setInterpolator(new DecelerateInterpolator());
animationSet.setDuration(ANIMATION_DURATION);
animationSet.setFillAfter(true);
mContentView.startAnimation(animationSet);
}
/**
* ChoiceItemBottomDialog从下往上升起的动画动画
*/
private void animateDown() {
if (mContentView == null) {
return;
}
TranslateAnimation translate = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 0f,
Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 1f
);
AlphaAnimation alpha = new AlphaAnimation(1, 0);
AnimationSet set = new AnimationSet(true);
set.addAnimation(translate);
set.addAnimation(alpha);
set.setInterpolator(new DecelerateInterpolator());
set.setDuration(ANIMATION_DURATION);
set.setFillAfter(true);
set.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
mIsAnimating = true;
}
@Override
public void onAnimationEnd(Animation animation) {
mIsAnimating = false;
mContentView.post(new Runnable() {
@Override
public void run() {
try {
AUILiveDialog.super.dismiss();
} catch (Exception e) {
Log.w("Test", "dismiss error\n" + Log.getStackTraceString(e));
}
}
});
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
mContentView.startAnimation(set);
}
private void fullScreenImmersive(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_FULLSCREEN;
view.setSystemUiVisibility(uiOptions);
}
}
@Override
public void show() {
setLayoutBySreenMode();
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
super.show();
fullScreenImmersive(getWindow().getDecorView());
this.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
animateUp();
}
@Override
public void dismiss() {
super.dismiss();
}
}

View File

@@ -0,0 +1,163 @@
package com.alivc.live.commonui.avdialog;
import android.content.Context;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.ViewCompat;
import com.alivc.live.commonui.R;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
public abstract class AVLiveBaseBottomSheetDialog extends AVLiveBaseDialog {
private ViewGroup mBottomSheetContainer;
private boolean mAnimateToCancel = false;
private boolean mAnimateToDismiss = false;
private BottomSheetBehavior<ViewGroup> mBottomSheetBehavior;
public AVLiveBaseBottomSheetDialog(Context context) {
super(context,R.style.Live_BottomSheetDialog);
}
public AVLiveBaseBottomSheetDialog(Context context, int theme) {
super(context, theme);
}
protected AVLiveBaseBottomSheetDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
}
@Override
public void initView(Context context) {
super.initView(context);
View container = getLayoutInflater().inflate(R.layout.av_live_base_dialog_bottom_sheet, null);
mBottomSheetContainer = container.findViewById(R.id.bottom_sheet_container);
View contentView = getContentView();
ViewGroup.LayoutParams contentParams = getContentLayoutParams();
if (contentView != null && contentParams != null) {
mBottomSheetContainer.addView(contentView, contentParams);
}
mBottomSheetBehavior = new BottomSheetBehavior<>();
mBottomSheetBehavior.setHideable(true);
mBottomSheetBehavior.addBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
if (mAnimateToCancel) {
cancel();
} else if (mAnimateToDismiss) {
dismiss();
} else {
cancel();
}
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
}
});
mBottomSheetBehavior.setPeekHeight(0);
mBottomSheetBehavior.setSkipCollapsed(true);
((CoordinatorLayout.LayoutParams) mBottomSheetContainer.getLayoutParams()).setBehavior(mBottomSheetBehavior);
container.findViewById(R.id.touch_outside).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mBottomSheetBehavior.getState() == BottomSheetBehavior.STATE_SETTLING) {
return;
}
if (mBottomSheetBehavior.isHideable() && isShowing()) {
cancel();
}
}
});
mBottomSheetContainer.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
setContentView(container, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
@Override
public void setCancelable(boolean flag) {
super.setCancelable(flag);
mBottomSheetBehavior.setHideable(flag);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Window window = getWindow();
if (window != null) {
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
ViewCompat.requestApplyInsets(mBottomSheetContainer);
}
}
@Override
protected void onStart() {
super.onStart();
if (mBottomSheetBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN) {
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
}
}
@Override
public void cancel() {
if (mBottomSheetBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN) {
mAnimateToCancel = false;
super.cancel();
} else {
mAnimateToCancel = true;
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
}
}
@Override
public void dismiss() {
if (mBottomSheetBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN){
mAnimateToDismiss = false;
super.dismiss();
}else {
mAnimateToDismiss = true;
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
}
}
@Override
public void show() {
super.show();
if (mBottomSheetBehavior.getState() != BottomSheetBehavior.STATE_EXPANDED) {
setToExpandWhenShow();
}
mAnimateToCancel = false;
mAnimateToDismiss = false;
}
private void setToExpandWhenShow() {
mBottomSheetContainer.postOnAnimation(new Runnable() {
@Override
public void run() {
mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}
});
}
protected abstract View getContentView();
protected abstract ViewGroup.LayoutParams getContentLayoutParams();
}

View File

@@ -0,0 +1,48 @@
package com.alivc.live.commonui.avdialog;
import android.app.Activity;
import android.content.Context;
import android.view.Window;
import androidx.appcompat.app.AppCompatDialog;
public class AVLiveBaseDialog extends AppCompatDialog {
public AVLiveBaseDialog(Context context) {
super(context);
initView(context);
}
public AVLiveBaseDialog(Context context, int theme) {
super(context, theme);
initView(context);
}
protected AVLiveBaseDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
initView(context);
}
public void initView(Context context) {
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
}
@Override
public void dismiss() {
Context context = getContext();
if (context instanceof Activity) {
Activity activity = (Activity) context;
if (activity.isDestroyed() || activity.isFinishing()) {
return;
}
super.dismiss();
} else {
try {
super.dismiss();
} catch (Throwable ignore) {
super.dismiss();
}
}
}
}

View File

@@ -0,0 +1,51 @@
package com.alivc.live.commonui.bean;
public class MusicInfo {
private String mMusicName;
private String mPlayTime;
private String mTotalTime;
private String mPath;
public MusicInfo() {
}
public MusicInfo(String musicName, String playTime, String totalTime, String path) {
mPlayTime = playTime;
mTotalTime = totalTime;
mMusicName = musicName;
mPath = path;
}
public String getPlayTime() {
return mPlayTime;
}
public void setPlayTime(String playTime) {
mPlayTime = playTime;
}
public String getTotalTime() {
return mTotalTime;
}
public void setTotalTime(String totalTime) {
mTotalTime = totalTime;
}
public String getMusicName() {
return mMusicName;
}
public void setMusicName(String musicName) {
mMusicName = musicName;
}
public String getPath() {
return mPath;
}
public void setPath(String path) {
mPath = path;
}
}

View File

@@ -0,0 +1,21 @@
package com.alivc.live.commonui.configview;
public class LivePushProgressStep {
public static final int PROGRESS_0 = 0;
public static final int PROGRESS_16 = 16;
public static final int PROGRESS_20 = 20;
public static final int PROGRESS_33 = 33;
public static final int PROGRESS_40 = 40;
public static final int PROGRESS_50 = 50;
public static final int PROGRESS_60 = 60;
public static final int PROGRESS_66 = 66;
public static final int PROGRESS_75 = 75;
public static final int PROGRESS_80 = 80;
public static final int PROGRESS_90 = 90;
public static final int PROGRESS_100 = 100;
public static final int PROGRESS_AUDIO_160 = 20;
public static final int PROGRESS_AUDIO_320 = 40;
public static final int PROGRESS_AUDIO_441 = 60;
public static final int PROGRESS_AUDIO_480 = 80;
}

View File

@@ -0,0 +1,168 @@
package com.alivc.live.commonui.configview;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.alivc.live.commonui.R;
import com.alivc.live.pusher.AlivcResolutionEnum;
public class LivePushResolutionView extends LinearLayout {
private SeekBar mResolution;
private TextView mResolutionText;
private LinearLayout mCustomResolutionRootView;
private AlivcResolutionEnum mDefinition = AlivcResolutionEnum.RESOLUTION_720P;
private OnResolutionChangedListener mOnResolutionChangedListener;
private EditText mWidth;
private EditText mHeight;
public LivePushResolutionView(Context context) {
this(context, null);
}
public LivePushResolutionView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, -1);
}
public LivePushResolutionView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
initListener();
}
private void init(Context context) {
View inflate = LayoutInflater.from(context).inflate(R.layout.push_resolution_view_layout, this, true);
mResolution = inflate.findViewById(R.id.resolution_seekbar);
mResolutionText = inflate.findViewById(R.id.resolution_text);
mWidth = inflate.findViewById(R.id.et_width);
mHeight = inflate.findViewById(R.id.et_height);
mCustomResolutionRootView = inflate.findViewById(R.id.custom_resolution_root);
}
private void initListener() {
mResolution.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean b) {
if (progress <= LivePushProgressStep.PROGRESS_0) {
mCustomResolutionRootView.setVisibility(View.GONE);
mDefinition = AlivcResolutionEnum.RESOLUTION_180P;
mResolutionText.setText(R.string.setting_resolution_180P);
} else if (progress > LivePushProgressStep.PROGRESS_0 && progress <= LivePushProgressStep.PROGRESS_16) {
mCustomResolutionRootView.setVisibility(View.GONE);
mDefinition = AlivcResolutionEnum.RESOLUTION_240P;
mResolutionText.setText(R.string.setting_resolution_240P);
} else if (progress > LivePushProgressStep.PROGRESS_16 && progress <= LivePushProgressStep.PROGRESS_33) {
mCustomResolutionRootView.setVisibility(View.GONE);
mDefinition = AlivcResolutionEnum.RESOLUTION_360P;
mResolutionText.setText(R.string.setting_resolution_360P);
} else if (progress > LivePushProgressStep.PROGRESS_33 && progress <= LivePushProgressStep.PROGRESS_50) {
mCustomResolutionRootView.setVisibility(View.GONE);
mDefinition = AlivcResolutionEnum.RESOLUTION_480P;
mResolutionText.setText(R.string.setting_resolution_480P);
} else if (progress > LivePushProgressStep.PROGRESS_50 && progress <= LivePushProgressStep.PROGRESS_66) {
mCustomResolutionRootView.setVisibility(View.GONE);
mDefinition = AlivcResolutionEnum.RESOLUTION_540P;
mResolutionText.setText(R.string.setting_resolution_540P);
} else if (progress > LivePushProgressStep.PROGRESS_66 && progress <= LivePushProgressStep.PROGRESS_75) {
mCustomResolutionRootView.setVisibility(View.GONE);
mDefinition = AlivcResolutionEnum.RESOLUTION_720P;
mResolutionText.setText(R.string.setting_resolution_720P);
} else if (progress > LivePushProgressStep.PROGRESS_75 && progress <= LivePushProgressStep.PROGRESS_90) {
mCustomResolutionRootView.setVisibility(View.GONE);
mDefinition = AlivcResolutionEnum.RESOLUTION_1080P;
mResolutionText.setText(R.string.setting_resolution_1080P);
} else {
mDefinition = AlivcResolutionEnum.RESOLUTION_SELF_DEFINE;
mResolutionText.setText(R.string.setting_resolution_self_define);
mCustomResolutionRootView.setVisibility(View.VISIBLE);
}
if (mOnResolutionChangedListener != null) {
mOnResolutionChangedListener.onResolutionChanged(mDefinition);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
int progress = seekBar.getProgress();
if (progress <= LivePushProgressStep.PROGRESS_0) {
seekBar.setProgress(0);
} else if (progress > LivePushProgressStep.PROGRESS_0 && progress <= LivePushProgressStep.PROGRESS_16) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_16);
} else if (progress > LivePushProgressStep.PROGRESS_16 && progress <= LivePushProgressStep.PROGRESS_33) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_33);
} else if (progress > LivePushProgressStep.PROGRESS_33 && progress <= LivePushProgressStep.PROGRESS_50) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_50);
} else if (progress > LivePushProgressStep.PROGRESS_50 && progress <= LivePushProgressStep.PROGRESS_66) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_66);
} else if (progress > LivePushProgressStep.PROGRESS_66 && progress <= LivePushProgressStep.PROGRESS_75) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_75);
} else if (progress > LivePushProgressStep.PROGRESS_75 && progress <= LivePushProgressStep.PROGRESS_90) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_90);
} else {
seekBar.setProgress(LivePushProgressStep.PROGRESS_100);
}
}
});
}
public void setResolution(AlivcResolutionEnum resolution) {
if (resolution == AlivcResolutionEnum.RESOLUTION_180P) {
mResolution.setProgress(LivePushProgressStep.PROGRESS_0);
mResolutionText.setText(getResources().getString(R.string.setting_resolution_180P));
} else if (resolution == AlivcResolutionEnum.RESOLUTION_240P) {
mResolution.setProgress(LivePushProgressStep.PROGRESS_16);
mResolutionText.setText(getResources().getString(R.string.setting_resolution_240P));
} else if (resolution == AlivcResolutionEnum.RESOLUTION_360P) {
mResolution.setProgress(LivePushProgressStep.PROGRESS_33);
mResolutionText.setText(getResources().getString(R.string.setting_resolution_360P));
} else if (resolution == AlivcResolutionEnum.RESOLUTION_480P) {
mResolution.setProgress(LivePushProgressStep.PROGRESS_50);
mResolutionText.setText(getResources().getString(R.string.setting_resolution_480P));
} else if (resolution == AlivcResolutionEnum.RESOLUTION_540P) {
mResolution.setProgress(LivePushProgressStep.PROGRESS_66);
mResolutionText.setText(getResources().getString(R.string.setting_resolution_540P));
} else if (resolution == AlivcResolutionEnum.RESOLUTION_720P) {
mResolution.setProgress(LivePushProgressStep.PROGRESS_75);
mResolutionText.setText(getResources().getString(R.string.setting_resolution_720P));
} else if (resolution == AlivcResolutionEnum.RESOLUTION_1080P) {
mResolution.setProgress(LivePushProgressStep.PROGRESS_90);
mResolutionText.setText(getResources().getString(R.string.setting_resolution_1080P));
} else if (resolution == AlivcResolutionEnum.RESOLUTION_SELF_DEFINE) {
mResolution.setProgress(LivePushProgressStep.PROGRESS_100);
mResolutionText.setText(getResources().getString(R.string.setting_resolution_self_define));
int resolutionWidth = AlivcResolutionEnum.getResolutionWidth(resolution, null);
int resolutionHeight = AlivcResolutionEnum.getResolutionHeight(resolution, null);
mWidth.setText(String.valueOf(resolutionWidth));
mHeight.setText(String.valueOf(resolutionHeight));
}
}
public int getSelfDefineWidth() {
return Integer.parseInt(mWidth.getText().toString());
}
public int getSelfDefineHeight() {
return Integer.parseInt(mHeight.getText().toString());
}
public void setOnResolutionChangedListener(OnResolutionChangedListener listener) {
this.mOnResolutionChangedListener = listener;
}
public interface OnResolutionChangedListener {
void onResolutionChanged(AlivcResolutionEnum resolutionEnum);
}
}

View File

@@ -0,0 +1,1220 @@
package com.alivc.live.commonui.configview;
import android.content.Context;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.alivc.live.annotations.AlivcLiveCameraCaptureOutputPreference;
import com.alivc.live.commonui.R;
import com.alivc.live.commonutils.TextFormatUtil;
import com.alivc.live.pusher.AlivcAudioAACProfileEnum;
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.AlivcEncodeType;
import com.alivc.live.pusher.AlivcFpsEnum;
import com.alivc.live.pusher.AlivcLivePushCameraTypeEnum;
import com.alivc.live.pusher.AlivcLivePushConfig;
import com.alivc.live.pusher.AlivcLivePushConstants;
import com.alivc.live.pusher.AlivcPreviewDisplayMode;
import com.alivc.live.pusher.AlivcPreviewOrientationEnum;
import com.alivc.live.pusher.AlivcQualityModeEnum;
import com.alivc.live.pusher.AlivcResolutionEnum;
import com.alivc.live.pusher.AlivcVideoEncodeGopEnum;
import java.io.File;
import java.util.Locale;
public class LivePushSettingView extends LinearLayout {
private com.alivc.live.commonui.databinding.PushSettingViewLayoutBinding mViewBinding;
private PushConfigDialogImpl mPushConfigDialog = new PushConfigDialogImpl();
//码率控制
private final MutableLiveData<Boolean> mBitrateControlLiveData = new MutableLiveData<>();
public LiveData<Boolean> bitrateControl = mBitrateControlLiveData;
//可变分辨率
private final MutableLiveData<Boolean> mVariableResolutionLiveData = new MutableLiveData<>();
public LiveData<Boolean> variableResolution = mVariableResolutionLiveData;
//分辨率
private final MutableLiveData<AlivcResolutionEnum> mResolutionLiveData = new MutableLiveData<>();
public LiveData<AlivcResolutionEnum> resolution = mResolutionLiveData;
//最小帧率
private final MutableLiveData<AlivcFpsEnum> mMinFpsLiveData = new MutableLiveData<>();
public LiveData<AlivcFpsEnum> minFps = mMinFpsLiveData;
//音频采样率
private final MutableLiveData<AlivcAudioSampleRateEnum> mAudioSampleRateLiveData = new MutableLiveData<>();
public LiveData<AlivcAudioSampleRateEnum> audioSampleRate = mAudioSampleRateLiveData;
//GOP
private final MutableLiveData<AlivcVideoEncodeGopEnum> mGopLiveData = new MutableLiveData<>();
public LiveData<AlivcVideoEncodeGopEnum> gop = mGopLiveData;
//FPS
private final MutableLiveData<AlivcFpsEnum> mFpsLiveData = new MutableLiveData<>();
public LiveData<AlivcFpsEnum> fps = mFpsLiveData;
//视频硬编码
private final MutableLiveData<Boolean> mVideoHardwareDecodeLiveData = new MutableLiveData<>();
public LiveData<Boolean> videoHardwareDecode = mVideoHardwareDecodeLiveData;
//音频硬编码
private final MutableLiveData<Boolean> mAudioHardwareDecodeLiveData = new MutableLiveData<>();
public LiveData<Boolean> audioHardwareDecode = mAudioHardwareDecodeLiveData;
//推流镜像
private final MutableLiveData<Boolean> mPushMirrorLiveData = new MutableLiveData<>();
public LiveData<Boolean> pushMirror = mPushMirrorLiveData;
//预览镜像
private final MutableLiveData<Boolean> mPreviewMirrorLiveData = new MutableLiveData<>();
public LiveData<Boolean> previewMirror = mPreviewMirrorLiveData;
//前置摄像头
private final MutableLiveData<Boolean> mEnableFrontCamera = new MutableLiveData<>();
public LiveData<Boolean> enableFrontCamera = mEnableFrontCamera;
//美颜
private final MutableLiveData<Boolean> mEnableBeauty = new MutableLiveData<>();
public LiveData<Boolean> enableBeauty = mEnableBeauty;
//暂停图片
private final MutableLiveData<Boolean> mPauseImageLiveData = new MutableLiveData<>();
public LiveData<Boolean> pauseImage = mPauseImageLiveData;
//网络差图片
private final MutableLiveData<Boolean> mNetWorkImageLiveData = new MutableLiveData<>();
public LiveData<Boolean> netWorkImage = mNetWorkImageLiveData;
//自动对焦
private final MutableLiveData<Boolean> mAutoFocusLiveData = new MutableLiveData<>();
public LiveData<Boolean> autoFocus = mAutoFocusLiveData;
//异步接口
private final MutableLiveData<Boolean> mAsyncLiveData = new MutableLiveData<>();
public LiveData<Boolean> async = mAsyncLiveData;
//音乐模式
private final MutableLiveData<Boolean> mMusicModeLiveData = new MutableLiveData<>();
public LiveData<Boolean> musicMode = mMusicModeLiveData;
//外部音视频
private final MutableLiveData<Boolean> mExternLiveData = new MutableLiveData<>();
public LiveData<Boolean> extern = mExternLiveData;
//本地日志
private final MutableLiveData<Boolean> mLocalLogLiveData = new MutableLiveData<>();
public LiveData<Boolean> localLog = mLocalLogLiveData;
//仅视频
private final MutableLiveData<Boolean> mVideoOnlyLiveData = new MutableLiveData<>();
public LiveData<Boolean> videoOnly = mVideoOnlyLiveData;
//仅音频
private final MutableLiveData<Boolean> mAudioOnlyLiveData = new MutableLiveData<>();
public LiveData<Boolean> audioOnly = mAudioOnlyLiveData;
//预览显示模式
private final MutableLiveData<AlivcPreviewDisplayMode> mPreviewDisplayModeLiveData = new MutableLiveData<>();
public LiveData<AlivcPreviewDisplayMode> previewDisplayMode = mPreviewDisplayModeLiveData;
//摄像头采集偏好
private final MutableLiveData<AlivcLiveCameraCaptureOutputPreference> mCameraCaptureOutputPreferenceLiveData = new MutableLiveData<>();
public LiveData<AlivcLiveCameraCaptureOutputPreference> cameraCaptureOutputPreference = mCameraCaptureOutputPreferenceLiveData;
//声道
private final MutableLiveData<AlivcAudioChannelEnum> mAudioChannelLiveData = new MutableLiveData<>();
public LiveData<AlivcAudioChannelEnum> audioChannel = mAudioChannelLiveData;
//音频编码
private final MutableLiveData<AlivcAudioAACProfileEnum> mAudioProfileLiveData = new MutableLiveData<>();
public LiveData<AlivcAudioAACProfileEnum> audioProfile = mAudioProfileLiveData;
//音频编码
private final MutableLiveData<AlivcEncodeType> mVideoEncodeTypeLiveData = new MutableLiveData<>();
public LiveData<AlivcEncodeType> videoEncodeType = mVideoEncodeTypeLiveData;
//B-Frame
private final MutableLiveData<Integer> mBFrameLiveData = new MutableLiveData<>();
public LiveData<Integer> bFrame = mBFrameLiveData;
// 视频目标码率
private final MutableLiveData<Integer> mTargetVideoBitrate = new MutableLiveData<>();
public LiveData<Integer> targetVideoBitrate = mTargetVideoBitrate;
// 视频最小码率
private final MutableLiveData<Integer> mMinVideoBitrate = new MutableLiveData<>();
public LiveData<Integer> minVideoBitrate = mMinVideoBitrate;
// 视频初始码率
private final MutableLiveData<Integer> mInitialVideoBitrate = new MutableLiveData<>();
public LiveData<Integer> initialVideoBitrate = mInitialVideoBitrate;
// 音频码率
private final MutableLiveData<Integer> mAudioBitrate = new MutableLiveData<>();
public LiveData<Integer> audioBitrate = mAudioBitrate;
//暂停图片路径
private final MutableLiveData<String> mPauseImgPathLiveData = new MutableLiveData<>();
public LiveData<String> pauseImagePath = mPauseImgPathLiveData;
//网络差图片路径
private final MutableLiveData<String> mNetWorkImgPathLiveData = new MutableLiveData<>();
public LiveData<String> netWorkImagePath = mNetWorkImgPathLiveData;
//屏幕方向
private final MutableLiveData<AlivcPreviewOrientationEnum> mPreviewOrientationLiveData = new MutableLiveData<>();
public LiveData<AlivcPreviewOrientationEnum> previewOrientation = mPreviewOrientationLiveData;
//视频质量
private final MutableLiveData<AlivcQualityModeEnum> mQualityModeLiveData = new MutableLiveData<>();
public LiveData<AlivcQualityModeEnum> qualityMode = mQualityModeLiveData;
//水印 Dialog
private final MutableLiveData<Boolean> mShowWaterMarkDialog = new MutableLiveData<>();
public LiveData<Boolean> showWaterMark = mShowWaterMarkDialog;
private AlivcQualityModeEnum mCurrentQualityMode = AlivcQualityModeEnum.QM_RESOLUTION_FIRST;
private AlivcResolutionEnum mCurrentResolution = AlivcResolutionEnum.RESOLUTION_540P;
private int mPushModeDefaultIndex = 0;
private int mDisplayModeDefaultIndex = 0;
private int mCameraCaptureOutputPreferenceDefaultIndex = 2;
private int mAudioChannelDefaultIndex = 1;
private int mAudioProfileDefaultIndex = 0;
private int mQualityModeDefaultIndex = 0;
private int mVideoEncoderTypeDefaultIndex = 0;
private int mBFrameDefaultIndex = 0;
private int mPreviewOrientationDefaultIndex = 0;
public LivePushSettingView(Context context) {
this(context, null);
}
public LivePushSettingView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, -1);
}
public LivePushSettingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
mViewBinding = com.alivc.live.commonui.databinding.PushSettingViewLayoutBinding.inflate(LayoutInflater.from(context), this, true);
initData();
initListener();
}
private void initData() {
mViewBinding.pushArgsSetting.targetRateEdit.setHint(String.valueOf(AlivcLivePushConstants.BITRATE_540P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate()));
mViewBinding.pushArgsSetting.targetRateEdit.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
mTargetVideoBitrate.setValue(TextFormatUtil.convertString2Int(String.valueOf(s)));
}
});
mViewBinding.pushArgsSetting.minRateEdit.setHint(String.valueOf(AlivcLivePushConstants.BITRATE_540P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate()));
mViewBinding.pushArgsSetting.minRateEdit.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
mMinVideoBitrate.setValue(TextFormatUtil.convertString2Int(String.valueOf(s)));
}
});
mViewBinding.pushArgsSetting.initRateEdit.setHint(String.valueOf(AlivcLivePushConstants.BITRATE_540P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate()));
mViewBinding.pushArgsSetting.initRateEdit.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
mInitialVideoBitrate.setValue(TextFormatUtil.convertString2Int(String.valueOf(s)));
}
});
turnOnBitRateFps(false);
}
private void initListener() {
//切换分辨率
mViewBinding.pushArgsSetting.resolutionView.setOnResolutionChangedListener(resolutionEnum -> {
mCurrentResolution = resolutionEnum;
mResolutionLiveData.setValue(resolutionEnum);
if (resolutionEnum == AlivcResolutionEnum.RESOLUTION_180P) {
if (mCurrentQualityMode.equals(AlivcQualityModeEnum.QM_RESOLUTION_FIRST)) {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_180P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_180P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_180P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
} else if (mCurrentQualityMode.equals(AlivcQualityModeEnum.QM_FLUENCY_FIRST)) {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_180P_FLUENCY_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_180P_FLUENCY_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_180P_FLUENCY_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
} else {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_180P.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_180P.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_180P.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
}
} else if (resolutionEnum == AlivcResolutionEnum.RESOLUTION_240P) {
if (mCurrentQualityMode.equals(AlivcQualityModeEnum.QM_RESOLUTION_FIRST)) {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_240P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_240P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_240P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
} else if (mCurrentQualityMode.equals(AlivcQualityModeEnum.QM_FLUENCY_FIRST)) {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_240P_FLUENCY_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_240P_FLUENCY_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_240P_FLUENCY_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
} else {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_240P.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_240P.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_240P.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
}
} else if (resolutionEnum == AlivcResolutionEnum.RESOLUTION_360P) {
if (mCurrentQualityMode.equals(AlivcQualityModeEnum.QM_RESOLUTION_FIRST)) {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_360P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_360P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_360P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
} else if (mCurrentQualityMode.equals(AlivcQualityModeEnum.QM_FLUENCY_FIRST)) {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_360P_FLUENCY_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_360P_FLUENCY_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_360P_FLUENCY_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
} else {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_360P.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_360P.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_360P.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
}
} else if (resolutionEnum == AlivcResolutionEnum.RESOLUTION_480P) {
if (mCurrentQualityMode.equals(AlivcQualityModeEnum.QM_RESOLUTION_FIRST)) {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_480P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_480P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_480P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
} else if (mCurrentQualityMode.equals(AlivcQualityModeEnum.QM_FLUENCY_FIRST)) {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_480P_FLUENCY_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_480P_FLUENCY_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_480P_FLUENCY_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
} else {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_480P.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_480P.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_480P.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
}
} else if (resolutionEnum == AlivcResolutionEnum.RESOLUTION_540P) {
if (mCurrentQualityMode.equals(AlivcQualityModeEnum.QM_RESOLUTION_FIRST)) {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_540P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_540P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_540P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
} else if (mCurrentQualityMode.equals(AlivcQualityModeEnum.QM_FLUENCY_FIRST)) {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_540P_FLUENCY_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_540P_FLUENCY_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_540P_FLUENCY_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
} else {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_540P.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_540P.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_540P.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
}
} else if (resolutionEnum == AlivcResolutionEnum.RESOLUTION_720P) {
if (mCurrentQualityMode.equals(AlivcQualityModeEnum.QM_RESOLUTION_FIRST)) {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_720P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_720P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_720P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
} else if (mCurrentQualityMode.equals(AlivcQualityModeEnum.QM_FLUENCY_FIRST)) {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_720P_FLUENCY_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_720P_FLUENCY_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_720P_FLUENCY_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
} else {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_720P.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_720P.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_720P.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
}
} else if (resolutionEnum == AlivcResolutionEnum.RESOLUTION_1080P) {
if (mCurrentQualityMode.equals(AlivcQualityModeEnum.QM_RESOLUTION_FIRST)) {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_1080P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_1080P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_1080P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
} else if (mCurrentQualityMode.equals(AlivcQualityModeEnum.QM_FLUENCY_FIRST)) {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_1080P_FLUENCY_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_1080P_FLUENCY_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_1080P_FLUENCY_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
} else {
changeVideoBitrateWithResolution(AlivcLivePushConstants.BITRATE_1080P.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_1080P.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_1080P.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate());
}
} else {
//自定义
changeVideoBitrateWithResolution(AlivcLivePushConstants.DEFAULT_VALUE_INT_TARGET_BITRATE,
AlivcLivePushConstants.DEFAULT_VALUE_INT_MIN_BITRATE,
AlivcLivePushConstants.DEFAULT_VALUE_INT_INIT_BITRATE);
}
});
//最小帧率
mViewBinding.pushArgsSetting.minFpsSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (progress <= LivePushProgressStep.PROGRESS_0) {
mMinFpsLiveData.setValue(AlivcFpsEnum.FPS_8);
mViewBinding.pushArgsSetting.minFpsText.setText(String.valueOf(AlivcFpsEnum.FPS_8.getFps()));
} else if (progress <= LivePushProgressStep.PROGRESS_16) {
mMinFpsLiveData.setValue(AlivcFpsEnum.FPS_10);
mViewBinding.pushArgsSetting.minFpsText.setText(String.valueOf(AlivcFpsEnum.FPS_10.getFps()));
} else if (progress <= LivePushProgressStep.PROGRESS_33) {
mMinFpsLiveData.setValue(AlivcFpsEnum.FPS_12);
mViewBinding.pushArgsSetting.minFpsText.setText(String.valueOf(AlivcFpsEnum.FPS_12.getFps()));
} else if (progress <= LivePushProgressStep.PROGRESS_50) {
mMinFpsLiveData.setValue(AlivcFpsEnum.FPS_15);
mViewBinding.pushArgsSetting.minFpsText.setText(String.valueOf(AlivcFpsEnum.FPS_15.getFps()));
} else if (progress <= LivePushProgressStep.PROGRESS_66) {
mMinFpsLiveData.setValue(AlivcFpsEnum.FPS_20);
mViewBinding.pushArgsSetting.minFpsText.setText(String.valueOf(AlivcFpsEnum.FPS_20.getFps()));
} else if (progress <= LivePushProgressStep.PROGRESS_80) {
mMinFpsLiveData.setValue(AlivcFpsEnum.FPS_25);
mViewBinding.pushArgsSetting.minFpsText.setText(String.valueOf(AlivcFpsEnum.FPS_25.getFps()));
} else {
mMinFpsLiveData.setValue(AlivcFpsEnum.FPS_30);
mViewBinding.pushArgsSetting.minFpsText.setText(String.valueOf(AlivcFpsEnum.FPS_30.getFps()));
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
//fps
mViewBinding.pushArgsSetting.fpsSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (progress <= LivePushProgressStep.PROGRESS_0) {
mFpsLiveData.setValue(AlivcFpsEnum.FPS_8);
mViewBinding.pushArgsSetting.fpsText.setText(String.valueOf(AlivcFpsEnum.FPS_8.getFps()));
} else if (progress <= LivePushProgressStep.PROGRESS_16) {
mFpsLiveData.setValue(AlivcFpsEnum.FPS_10);
mViewBinding.pushArgsSetting.fpsText.setText(String.valueOf(AlivcFpsEnum.FPS_10.getFps()));
} else if (progress <= LivePushProgressStep.PROGRESS_33) {
mFpsLiveData.setValue(AlivcFpsEnum.FPS_12);
mViewBinding.pushArgsSetting.fpsText.setText(String.valueOf(AlivcFpsEnum.FPS_12.getFps()));
} else if (progress <= LivePushProgressStep.PROGRESS_50) {
mFpsLiveData.setValue(AlivcFpsEnum.FPS_15);
mViewBinding.pushArgsSetting.fpsText.setText(String.valueOf(AlivcFpsEnum.FPS_15.getFps()));
} else if (progress <= LivePushProgressStep.PROGRESS_66) {
mFpsLiveData.setValue(AlivcFpsEnum.FPS_20);
mViewBinding.pushArgsSetting.fpsText.setText(String.valueOf(AlivcFpsEnum.FPS_20.getFps()));
} else if (progress <= LivePushProgressStep.PROGRESS_80) {
mFpsLiveData.setValue(AlivcFpsEnum.FPS_25);
mViewBinding.pushArgsSetting.fpsText.setText(String.valueOf(AlivcFpsEnum.FPS_25.getFps()));
} else {
mFpsLiveData.setValue(AlivcFpsEnum.FPS_30);
mViewBinding.pushArgsSetting.fpsText.setText(String.valueOf(AlivcFpsEnum.FPS_30.getFps()));
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
int progress = seekBar.getProgress();
if (progress <= LivePushProgressStep.PROGRESS_0) {
seekBar.setProgress(0);
} else if (progress <= LivePushProgressStep.PROGRESS_16) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_16);
} else if (progress <= LivePushProgressStep.PROGRESS_33) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_33);
} else if (progress <= LivePushProgressStep.PROGRESS_50) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_50);
} else if (progress <= LivePushProgressStep.PROGRESS_66) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_66);
} else if (progress <= LivePushProgressStep.PROGRESS_80) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_80);
} else {
seekBar.setProgress(LivePushProgressStep.PROGRESS_100);
}
}
});
//音频采样率
mViewBinding.pushArgsSetting.audioRateSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (progress <= LivePushProgressStep.PROGRESS_AUDIO_160) {
mAudioSampleRateLiveData.setValue(AlivcAudioSampleRateEnum.AUDIO_SAMPLE_RATE_16000);
mViewBinding.pushArgsSetting.audioRateText.setText(getContext().getString(R.string.setting_audio_160));
} else if (progress <= LivePushProgressStep.PROGRESS_AUDIO_320) {
mAudioSampleRateLiveData.setValue(AlivcAudioSampleRateEnum.AUDIO_SAMPLE_RATE_32000);
mViewBinding.pushArgsSetting.audioRateText.setText(getContext().getString(R.string.setting_audio_320));
} else if (progress <= LivePushProgressStep.PROGRESS_AUDIO_441) {
mAudioSampleRateLiveData.setValue(AlivcAudioSampleRateEnum.AUDIO_SAMPLE_RATE_44100);
mViewBinding.pushArgsSetting.audioRateText.setText(getContext().getString(R.string.setting_audio_441));
} else {
mAudioSampleRateLiveData.setValue(AlivcAudioSampleRateEnum.AUDIO_SAMPLE_RATE_48000);
mViewBinding.pushArgsSetting.audioRateText.setText(getContext().getString(R.string.setting_audio_480));
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
int progress = seekBar.getProgress();
if (progress <= LivePushProgressStep.PROGRESS_AUDIO_160) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_AUDIO_160);
} else if (progress <= LivePushProgressStep.PROGRESS_AUDIO_320) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_AUDIO_320);
} else if (progress <= LivePushProgressStep.PROGRESS_AUDIO_441) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_AUDIO_441);
} else {
seekBar.setProgress(LivePushProgressStep.PROGRESS_AUDIO_480);
}
}
});
//GOP
mViewBinding.pushArgsSetting.gopSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (progress <= LivePushProgressStep.PROGRESS_20) {
mGopLiveData.setValue(AlivcVideoEncodeGopEnum.GOP_ONE);
mViewBinding.pushArgsSetting.gopText.setText("1/s");
} else if (progress <= LivePushProgressStep.PROGRESS_40) {
mGopLiveData.setValue(AlivcVideoEncodeGopEnum.GOP_TWO);
mViewBinding.pushArgsSetting.gopText.setText("2/s");
} else if (progress <= LivePushProgressStep.PROGRESS_60) {
mGopLiveData.setValue(AlivcVideoEncodeGopEnum.GOP_THREE);
mViewBinding.pushArgsSetting.gopText.setText("3/s");
} else if (progress <= LivePushProgressStep.PROGRESS_80) {
mGopLiveData.setValue(AlivcVideoEncodeGopEnum.GOP_FOUR);
mViewBinding.pushArgsSetting.gopText.setText("4/s");
} else if (progress <= LivePushProgressStep.PROGRESS_100) {
mGopLiveData.setValue(AlivcVideoEncodeGopEnum.GOP_FIVE);
mViewBinding.pushArgsSetting.gopText.setText("5/s");
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
int progress = seekBar.getProgress();
if (progress <= 20) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_20);
} else if (progress <= 40) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_40);
} else if (progress <= 60) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_60);
} else if (progress <= 80) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_80);
} else if (progress <= 100) {
seekBar.setProgress(LivePushProgressStep.PROGRESS_100);
}
}
});
//码率控制
mViewBinding.pushArgsSetting.bitrateControl.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mBitrateControlLiveData.setValue(isChecked);
});
//可变分辨率
mViewBinding.pushArgsSetting.variableResolution.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mVariableResolutionLiveData.setValue(isChecked);
});
//高级设置
mViewBinding.pushArgsSetting.advanceConfig.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mViewBinding.pushArgsSetting.advanceLayout.setVisibility(isChecked ? View.VISIBLE : View.GONE);
});
//显示模式
mViewBinding.pushArgsSetting.qualityModes.setOnClickListener(view -> {
mPushConfigDialog.showConfigDialog(mViewBinding.pushArgsSetting.qualityModes, mQualityListener, mQualityModeDefaultIndex);
});
//音频编码
mViewBinding.pushArgsSetting.audioProfiles.setOnClickListener(view -> {
mPushConfigDialog.showConfigDialog(mViewBinding.pushArgsSetting.audioProfiles, mAudioProfilesListener, mAudioProfileDefaultIndex);
});
//声道
mViewBinding.pushArgsSetting.audioChannel.setOnClickListener(view -> {
mPushConfigDialog.showConfigDialog(mViewBinding.pushArgsSetting.audioChannel, mAudioChannelListener, mAudioChannelDefaultIndex);
});
//视频编码
mViewBinding.pushArgsSetting.videoEncoderType.setOnClickListener(view -> {
mPushConfigDialog.showConfigDialog(mViewBinding.pushArgsSetting.videoEncoderType, mEncoderTypeListener, mVideoEncoderTypeDefaultIndex);
});
//B帧
mViewBinding.pushArgsSetting.bFrameNum.setOnClickListener(view -> {
mPushConfigDialog.showConfigDialog(mViewBinding.pushArgsSetting.bFrameNum, mFrameNumListener, mBFrameDefaultIndex);
});
//视频硬编码
mViewBinding.pushArgsSetting.hardSwitch.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mVideoHardwareDecodeLiveData.setValue(isChecked);
});
//音频硬编码
mViewBinding.pushArgsSetting.audioHardenc.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mAudioHardwareDecodeLiveData.setValue(isChecked);
});
//推流方向
mViewBinding.pushFunctionSetting.mainOrientation.setOnClickListener(view -> {
mPushConfigDialog.showConfigDialog(mViewBinding.pushFunctionSetting.mainOrientation, mOrientationListener, mPreviewOrientationDefaultIndex);
});
//显示模式
mViewBinding.pushFunctionSetting.settingDisplayMode.setOnClickListener(view -> {
mPushConfigDialog.showConfigDialog(mViewBinding.pushFunctionSetting.settingDisplayMode, mDisplayModeListener, mDisplayModeDefaultIndex);
});
//摄像头采集偏好
mViewBinding.pushFunctionSetting.settingCameraCaptureOutputPreference.setOnClickListener(view -> {
mPushConfigDialog.showConfigDialog(mViewBinding.pushFunctionSetting.settingCameraCaptureOutputPreference, mCameraCaptureOutputPreferenceListener, mCameraCaptureOutputPreferenceDefaultIndex);
});
//水印
mViewBinding.pushFunctionSetting.watermarkSwitch.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mViewBinding.pushFunctionSetting.waterPosition.setClickable(isChecked);
mViewBinding.pushFunctionSetting.waterPosition.setTextColor(isChecked ? getResources().getColor(R.color.colourful_text_strong) : getResources().getColor(R.color.text_ultraweak));
});
//水印位置
mViewBinding.pushFunctionSetting.waterPosition.setOnClickListener(view -> {
mShowWaterMarkDialog.setValue(mViewBinding.pushFunctionSetting.watermarkSwitch.isChecked());
});
//推流镜像
mViewBinding.pushFunctionSetting.pushMirrorSwitch.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mPushMirrorLiveData.setValue(isChecked);
});
//预览镜像
mViewBinding.pushFunctionSetting.previewMirrorSwitch.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mPreviewMirrorLiveData.setValue(isChecked);
});
//摄像头
mViewBinding.pushFunctionSetting.cameraSwitch.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mEnableFrontCamera.setValue(isChecked);
});
//自动对焦
mViewBinding.pushFunctionSetting.autofocusSwitch.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mAutoFocusLiveData.setValue(isChecked);
});
//美颜
mViewBinding.pushFunctionSetting.beautyOnSwitch.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mEnableBeauty.setValue(isChecked);
});
//暂停推图片
mViewBinding.pushFunctionSetting.pauseImage.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mPauseImageLiveData.setValue(isChecked);
});
//网络差图片
mViewBinding.pushFunctionSetting.networkImage.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mNetWorkImageLiveData.setValue(isChecked);
});
//推流模式
mViewBinding.pushFunctionSetting.pushMode.setOnClickListener(view -> {
mPushConfigDialog.showConfigDialog(mViewBinding.pushFunctionSetting.pushMode, mPushModeListener, mPushModeDefaultIndex);
});
//有效时长,鉴权 key
//异步接口
mViewBinding.pushFunctionSetting.asyncSwitch.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mAsyncLiveData.setValue(isChecked);
});
//音乐模式
mViewBinding.pushFunctionSetting.musicModeSwitch.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mMusicModeLiveData.setValue(isChecked);
});
//外部音视频
mViewBinding.pushFunctionSetting.externVideo.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mExternLiveData.setValue(isChecked);
// 当前外部音视频的资源是720P因此推流分辨率对应调整为720P
mViewBinding.pushArgsSetting.resolutionView.setResolution(AlivcResolutionEnum.RESOLUTION_720P);
});
//本地日志
mViewBinding.pushFunctionSetting.logSwitch.setOnCheckedChangeListener((compoundButton, isChecked) -> {
mLocalLogLiveData.setValue(isChecked);
});
}
public void showArgsContent(boolean isShow) {
mViewBinding.pushArgsSetting.getRoot().setVisibility(isShow ? View.VISIBLE : View.GONE);
mViewBinding.pushFunctionSetting.getRoot().setVisibility(isShow ? View.GONE : View.VISIBLE);
}
private final PushConfigBottomSheetLive.OnPushConfigSelectorListener mQualityListener = new PushConfigBottomSheetLive.OnPushConfigSelectorListener() {
@Override
public void confirm(String tips, int i) {
mViewBinding.pushArgsSetting.qualityModes.setText(tips);
if (AlivcQualityModeEnum.QM_CUSTOM.equals(mCurrentQualityMode)) {
mViewBinding.pushArgsSetting.targetRateEdit.setText("");
mViewBinding.pushArgsSetting.minRateEdit.setText("");
mViewBinding.pushArgsSetting.initRateEdit.setText("");
mViewBinding.pushArgsSetting.audioBitrate.setText("");
}
if (i == 0) {
mCurrentQualityMode = AlivcQualityModeEnum.QM_RESOLUTION_FIRST;
mQualityModeLiveData.setValue(AlivcQualityModeEnum.QM_RESOLUTION_FIRST);
if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_180P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_180P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_180P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_180P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
} else if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_240P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_240P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_240P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_240P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
} else if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_360P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_360P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_360P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_360P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
} else if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_480P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_480P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_480P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_480P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
} else if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_540P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_540P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_540P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_540P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
} else if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_720P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_720P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_720P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_720P_RESOLUTION_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
}
turnOnBitRateFps(false);
} else if (i == 1) {
mCurrentQualityMode = AlivcQualityModeEnum.QM_FLUENCY_FIRST;
mQualityModeLiveData.setValue(AlivcQualityModeEnum.QM_FLUENCY_FIRST);
if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_180P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_180P_FLUENCY_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_180P_FLUENCY_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_180P_FLUENCY_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
} else if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_240P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_240P_FLUENCY_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_240P_FLUENCY_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_240P_FLUENCY_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
} else if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_360P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_360P_FLUENCY_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_360P_FLUENCY_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_360P_FLUENCY_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
} else if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_480P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_480P_FLUENCY_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_480P_FLUENCY_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_480P_FLUENCY_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
} else if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_540P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_540P_FLUENCY_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_540P_FLUENCY_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_540P_FLUENCY_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
} else if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_720P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_720P_FLUENCY_FIRST.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_720P_FLUENCY_FIRST.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_720P_FLUENCY_FIRST.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
}
turnOnBitRateFps(false);
} else if (i == 2) {
mCurrentQualityMode = AlivcQualityModeEnum.QM_CUSTOM;
mQualityModeLiveData.setValue(AlivcQualityModeEnum.QM_CUSTOM);
if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_180P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_180P.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_180P.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_180P.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
} else if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_240P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_240P.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_240P.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_240P.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
} else if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_360P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_360P.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_360P.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_360P.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
} else if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_480P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_480P.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_480P.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_480P.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
} else if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_540P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_540P.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_540P.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_540P.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
} else if (mCurrentResolution.equals(AlivcResolutionEnum.RESOLUTION_720P)) {
changeVideoBitrateWithResolution(
AlivcLivePushConstants.BITRATE_720P.DEFAULT_VALUE_INT_TARGET_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_720P.DEFAULT_VALUE_INT_MIN_BITRATE.getBitrate(),
AlivcLivePushConstants.BITRATE_720P.DEFAULT_VALUE_INT_INIT_BITRATE.getBitrate()
);
}
turnOnBitRateFps(true);
}
}
};
private final PushConfigBottomSheetLive.OnPushConfigSelectorListener mAudioProfilesListener = (data, index) -> {
if (index == 0) {
mAudioProfileLiveData.setValue(AlivcAudioAACProfileEnum.AAC_LC);
} else if (index == 1) {
mAudioProfileLiveData.setValue(AlivcAudioAACProfileEnum.HE_AAC);
} else if (index == 2) {
mAudioProfileLiveData.setValue(AlivcAudioAACProfileEnum.HE_AAC_v2);
} else if (index == 3) {
mAudioProfileLiveData.setValue(AlivcAudioAACProfileEnum.AAC_LD);
}
};
private void turnOnBitRateFps(boolean on) {
if (!on) {
mViewBinding.pushArgsSetting.fpsSeekbar.setProgress(83);
mViewBinding.pushArgsSetting.fpsText.setText(String.valueOf(AlivcFpsEnum.FPS_25.getFps()));
mViewBinding.pushArgsSetting.targetRateEdit.setFocusable(false);
mViewBinding.pushArgsSetting.minRateEdit.setFocusable(false);
mViewBinding.pushArgsSetting.initRateEdit.setFocusable(false);
mViewBinding.pushArgsSetting.fpsSeekbar.setFocusable(false);
mViewBinding.pushArgsSetting.targetRateEdit.setFocusableInTouchMode(false);
mViewBinding.pushArgsSetting.minRateEdit.setFocusableInTouchMode(false);
mViewBinding.pushArgsSetting.initRateEdit.setFocusableInTouchMode(false);
mViewBinding.pushArgsSetting.fpsSeekbar.setFocusableInTouchMode(false);
} else {
mViewBinding.pushArgsSetting.targetRateEdit.setFocusable(true);
mViewBinding.pushArgsSetting.minRateEdit.setFocusable(true);
mViewBinding.pushArgsSetting.initRateEdit.setFocusable(true);
mViewBinding.pushArgsSetting.targetRateEdit.setFocusableInTouchMode(true);
mViewBinding.pushArgsSetting.minRateEdit.setFocusableInTouchMode(true);
mViewBinding.pushArgsSetting.initRateEdit.setFocusableInTouchMode(true);
mViewBinding.pushArgsSetting.targetRateEdit.requestFocus();
mViewBinding.pushArgsSetting.initRateEdit.requestFocus();
mViewBinding.pushArgsSetting.minRateEdit.requestFocus();
}
}
private void changeVideoBitrateWithResolution(int targetBitrate, int minBitrate, int initBitrate) {
mViewBinding.pushArgsSetting.targetRateEdit.setHint(String.valueOf(targetBitrate));
mViewBinding.pushArgsSetting.minRateEdit.setHint(String.valueOf(minBitrate));
mViewBinding.pushArgsSetting.initRateEdit.setHint(String.valueOf(initBitrate));
mTargetVideoBitrate.setValue(targetBitrate);
mMinVideoBitrate.setValue(minBitrate);
mInitialVideoBitrate.setValue(initBitrate);
}
private final PushConfigBottomSheetLive.OnPushConfigSelectorListener mAudioChannelListener = (data, index) -> {
if (index == 0) {
mAudioChannelLiveData.setValue(AlivcAudioChannelEnum.AUDIO_CHANNEL_ONE);
} else if (index == 1) {
mAudioChannelLiveData.setValue(AlivcAudioChannelEnum.AUDIO_CHANNEL_TWO);
}
};
private final PushConfigBottomSheetLive.OnPushConfigSelectorListener mEncoderTypeListener = (data, index) -> {
if (index == 0) {
mVideoEncodeTypeLiveData.setValue(AlivcEncodeType.Encode_TYPE_H264);
} else if (index == 1) {
mVideoEncodeTypeLiveData.setValue(AlivcEncodeType.Encode_TYPE_H265);
}
};
private final PushConfigBottomSheetLive.OnPushConfigSelectorListener mFrameNumListener = (data, index) -> {
if (index == 0) {
mBFrameLiveData.setValue(0);
} else if (index == 1) {
mBFrameLiveData.setValue(1);
} else if (index == 2) {
mBFrameLiveData.setValue(3);
}
};
private final PushConfigBottomSheetLive.OnPushConfigSelectorListener mOrientationListener = (data, index) -> {
if (index == 0) {
mPreviewOrientationLiveData.setValue(AlivcPreviewOrientationEnum.ORIENTATION_PORTRAIT);
} else if (index == 1) {
mPreviewOrientationLiveData.setValue(AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_LEFT);
} else if (index == 2) {
mPreviewOrientationLiveData.setValue(AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_RIGHT);
}
mPauseImgPathLiveData.setValue(getContext().getFilesDir().getPath() + File.separator + "alivc_resource/background_push.png");
mNetWorkImgPathLiveData.setValue(getContext().getFilesDir().getPath() + File.separator + "alivc_resource/poor_network.png");
};
private final PushConfigBottomSheetLive.OnPushConfigSelectorListener mDisplayModeListener = (data, index) -> {
if (index == 0) {
mPreviewDisplayModeLiveData.setValue(AlivcPreviewDisplayMode.ALIVC_LIVE_PUSHER_PREVIEW_SCALE_FILL);
} else if (index == 1) {
mPreviewDisplayModeLiveData.setValue(AlivcPreviewDisplayMode.ALIVC_LIVE_PUSHER_PREVIEW_ASPECT_FIT);
} else if (index == 2) {
mPreviewDisplayModeLiveData.setValue(AlivcPreviewDisplayMode.ALIVC_LIVE_PUSHER_PREVIEW_ASPECT_FILL);
}
};
private final PushConfigBottomSheetLive.OnPushConfigSelectorListener mCameraCaptureOutputPreferenceListener = (data, index) -> {
if (index == 0) {
mCameraCaptureOutputPreferenceLiveData.setValue(AlivcLiveCameraCaptureOutputPreference.AUTO);
} else if (index == 1) {
mCameraCaptureOutputPreferenceLiveData.setValue(AlivcLiveCameraCaptureOutputPreference.PERFORMANCE);
} else if (index == 2) {
mCameraCaptureOutputPreferenceLiveData.setValue(AlivcLiveCameraCaptureOutputPreference.PREVIEW);
}
};
private final PushConfigBottomSheetLive.OnPushConfigSelectorListener mPushModeListener = (data, index) -> {
boolean mAudioOnlyPush = (index == 1);
boolean mVideoOnlyPush = (index == 2);
mAudioOnlyLiveData.setValue(mAudioOnlyPush);
mVideoOnlyLiveData.setValue(mVideoOnlyPush);
};
public String getInitVideoBitrate() {
String initVideoBitrate = mViewBinding.pushArgsSetting.initRateEdit.getText().toString();
if (TextUtils.isEmpty(initVideoBitrate)) {
return mViewBinding.pushArgsSetting.initRateEdit.getHint().toString();
}
return initVideoBitrate;
}
public String getAudioBitrate() {
String audioBitrate = mViewBinding.pushArgsSetting.audioBitrate.getText().toString();
if (TextUtils.isEmpty(audioBitrate)) {
return mViewBinding.pushArgsSetting.audioBitrate.getHint().toString();
}
return audioBitrate;
}
public String getMinVideoBitrate() {
String minVideoBitrate = mViewBinding.pushArgsSetting.minRateEdit.getText().toString();
if (TextUtils.isEmpty(minVideoBitrate)) {
return mViewBinding.pushArgsSetting.minRateEdit.getHint().toString();
}
return minVideoBitrate;
}
public String getMinVideoBitrateOnlyEditText() {
return mViewBinding.pushArgsSetting.minRateEdit.getText().toString();
}
public String getMinVideoBitrateOnlyHint() {
return mViewBinding.pushArgsSetting.minRateEdit.getHint().toString();
}
public void setTargetVideoBitrateText(String bitrate) {
mViewBinding.pushArgsSetting.targetRateEdit.setText(bitrate);
}
public void setMinVideoBitrateText(String bitrate) {
mViewBinding.pushArgsSetting.minRateEdit.setText(bitrate);
}
public String getTargetVideoBitrate() {
String targetVideoBitrate = mViewBinding.pushArgsSetting.targetRateEdit.getText().toString();
if (TextUtils.isEmpty(targetVideoBitrate)) {
return mViewBinding.pushArgsSetting.targetRateEdit.getHint().toString();
}
return targetVideoBitrate;
}
public String getTargetVideoBitrateOnlyEditText() {
return mViewBinding.pushArgsSetting.targetRateEdit.getText().toString();
}
public String getTargetVideoBitrateOnlyHint() {
return mViewBinding.pushArgsSetting.targetRateEdit.getHint().toString();
}
public int getRetryCount() {
String retryCount = mViewBinding.pushFunctionSetting.retryCount.getText().toString();
if (TextUtils.isEmpty(retryCount)) {
return AlivcLivePushConstants.DEFAULT_VALUE_INT_AUDIO_RETRY_COUNT;
}
return Integer.parseInt(retryCount);
}
public int getRetryInterval() {
String retryInterval = mViewBinding.pushFunctionSetting.retryInterval.getText().toString();
if (TextUtils.isEmpty(retryInterval)) {
return AlivcLivePushConstants.DEFAULT_VALUE_INT_RETRY_INTERVAL;
}
return Integer.parseInt(retryInterval);
}
public String getAuthTime() {
return mViewBinding.pushFunctionSetting.authTime.getText().toString();
}
public String getPrivacyKey() {
return mViewBinding.pushFunctionSetting.privacyKey.getText().toString();
}
public void externDownloadError() {
mViewBinding.pushFunctionSetting.externVideo.setChecked(false);
}
public boolean enableWaterMark() {
return mViewBinding.pushFunctionSetting.watermarkSwitch.isChecked();
}
public void setPushMirror(boolean isChecked) {
mViewBinding.pushFunctionSetting.pushMirrorSwitch.setChecked(isChecked);
}
public void setPreviewMirror(boolean isChecked) {
mViewBinding.pushFunctionSetting.previewMirrorSwitch.setChecked(isChecked);
}
public void setAutoFocus(boolean isChecked) {
mViewBinding.pushFunctionSetting.autofocusSwitch.setChecked(isChecked);
}
public void setBeautyOn(boolean isChecked) {
mViewBinding.pushFunctionSetting.beautyOnSwitch.setChecked(isChecked);
}
public boolean enableBeauty() {
return mViewBinding.pushFunctionSetting.beautyOnSwitch.isChecked();
}
public int getSelfDefineResolutionWidth() {
return mViewBinding.pushArgsSetting.resolutionView.getSelfDefineWidth();
}
public int getSelfDefineResolutionHeight() {
return mViewBinding.pushArgsSetting.resolutionView.getSelfDefineHeight();
}
public void destroy() {
mPushConfigDialog.destroy();
}
public void setDefaultConfig(AlivcLivePushConfig alivcLivePushConfig, boolean isEnableBeauty, boolean isEnableLocalLog, boolean isEnableWaterMark) {
mViewBinding.pushArgsSetting.bitrateControl.setChecked(alivcLivePushConfig.isEnableBitrateControl());
mViewBinding.pushArgsSetting.targetRateEdit.setText(String.valueOf(alivcLivePushConfig.getTargetVideoBitrate()));
mViewBinding.pushArgsSetting.minRateEdit.setText(String.valueOf(alivcLivePushConfig.getMinVideoBitrate()));
mViewBinding.pushArgsSetting.variableResolution.setChecked(alivcLivePushConfig.isEnableAutoResolution());
AlivcResolutionEnum configResolution = alivcLivePushConfig.getResolution();
mViewBinding.pushArgsSetting.resolutionView.setResolution(configResolution);
int audioChannels = alivcLivePushConfig.getAudioChannels();
if (audioChannels == AlivcAudioChannelEnum.AUDIO_CHANNEL_ONE.getChannelCount()) {
mAudioChannelDefaultIndex = 0;
mViewBinding.pushArgsSetting.audioChannel.setText(getResources().getString(R.string.single_track));
} else if ((audioChannels == AlivcAudioChannelEnum.AUDIO_CHANNEL_TWO.getChannelCount())) {
mAudioChannelDefaultIndex = 1;
mViewBinding.pushArgsSetting.audioChannel.setText(getResources().getString(R.string.dual_track));
}
AlivcAudioAACProfileEnum configAudioProfile = alivcLivePushConfig.getAudioProfile();
if (configAudioProfile == AlivcAudioAACProfileEnum.AAC_LC) {
mAudioProfileDefaultIndex = 0;
mViewBinding.pushArgsSetting.audioProfiles.setText(getResources().getString(R.string.setting_audio_aac_lc));
} else if (configAudioProfile == AlivcAudioAACProfileEnum.HE_AAC) {
mAudioProfileDefaultIndex = 1;
mViewBinding.pushArgsSetting.audioProfiles.setText(getResources().getString(R.string.setting_audio_aac_he));
} else if (configAudioProfile == AlivcAudioAACProfileEnum.HE_AAC_v2) {
mAudioProfileDefaultIndex = 2;
mViewBinding.pushArgsSetting.audioProfiles.setText(getResources().getString(R.string.setting_audio_aac_hev2));
} else if (configAudioProfile == AlivcAudioAACProfileEnum.AAC_LD) {
mAudioProfileDefaultIndex = 3;
mViewBinding.pushArgsSetting.audioProfiles.setText(getResources().getString(R.string.setting_audio_aac_ld));
}
mViewBinding.pushArgsSetting.minFpsText.setText(String.valueOf(alivcLivePushConfig.getMinFps()));
int configMinFps = alivcLivePushConfig.getMinFps();
if (configMinFps == AlivcFpsEnum.FPS_8.getFps()) {
mViewBinding.pushArgsSetting.minFpsSeekbar.setProgress(LivePushProgressStep.PROGRESS_0);
} else if (configMinFps == AlivcFpsEnum.FPS_10.getFps()) {
mViewBinding.pushArgsSetting.minFpsSeekbar.setProgress(LivePushProgressStep.PROGRESS_16);
} else if (configMinFps == AlivcFpsEnum.FPS_12.getFps()) {
mViewBinding.pushArgsSetting.minFpsSeekbar.setProgress(LivePushProgressStep.PROGRESS_33);
} else if (configMinFps == AlivcFpsEnum.FPS_15.getFps()) {
mViewBinding.pushArgsSetting.minFpsSeekbar.setProgress(LivePushProgressStep.PROGRESS_50);
} else if (configMinFps == AlivcFpsEnum.FPS_20.getFps()) {
mViewBinding.pushArgsSetting.minFpsSeekbar.setProgress(LivePushProgressStep.PROGRESS_66);
} else if (configMinFps == AlivcFpsEnum.FPS_25.getFps()) {
mViewBinding.pushArgsSetting.minFpsSeekbar.setProgress(LivePushProgressStep.PROGRESS_80);
} else if (configMinFps == AlivcFpsEnum.FPS_30.getFps()) {
mViewBinding.pushArgsSetting.minFpsSeekbar.setProgress(LivePushProgressStep.PROGRESS_100);
}
AlivcAudioSampleRateEnum configAudioSampleRate = alivcLivePushConfig.getAudioSampleRate();
if (configAudioSampleRate == AlivcAudioSampleRateEnum.AUDIO_SAMPLE_RATE_16000) {
mViewBinding.pushArgsSetting.audioRateSeekbar.setProgress(LivePushProgressStep.PROGRESS_AUDIO_160);
mViewBinding.pushArgsSetting.audioRateText.setText(getResources().getString(R.string.setting_audio_160));
} else if (configAudioSampleRate == AlivcAudioSampleRateEnum.AUDIO_SAMPLE_RATE_32000) {
mViewBinding.pushArgsSetting.audioRateSeekbar.setProgress(LivePushProgressStep.PROGRESS_AUDIO_320);
mViewBinding.pushArgsSetting.audioRateText.setText(getResources().getString(R.string.setting_audio_320));
} else if (configAudioSampleRate == AlivcAudioSampleRateEnum.AUDIO_SAMPLE_RATE_44100) {
mViewBinding.pushArgsSetting.audioRateSeekbar.setProgress(LivePushProgressStep.PROGRESS_AUDIO_441);
mViewBinding.pushArgsSetting.audioRateText.setText(getResources().getString(R.string.setting_audio_441));
} else if (configAudioSampleRate == AlivcAudioSampleRateEnum.AUDIO_SAMPLE_RATE_48000) {
mViewBinding.pushArgsSetting.audioRateSeekbar.setProgress(LivePushProgressStep.PROGRESS_AUDIO_480);
mViewBinding.pushArgsSetting.audioRateText.setText(getResources().getString(R.string.setting_audio_480));
}
mViewBinding.pushArgsSetting.gopText.setText(String.format(Locale.getDefault(), "%d/s", alivcLivePushConfig.getVideoEncodeGop()));
int videoEncodeGop = alivcLivePushConfig.getVideoEncodeGop();
if (videoEncodeGop == AlivcVideoEncodeGopEnum.GOP_ONE.getGop()) {
mViewBinding.pushArgsSetting.gopSeekbar.setProgress(LivePushProgressStep.PROGRESS_20);
} else if (videoEncodeGop == AlivcVideoEncodeGopEnum.GOP_TWO.getGop()) {
mViewBinding.pushArgsSetting.gopSeekbar.setProgress(LivePushProgressStep.PROGRESS_40);
} else if (videoEncodeGop == AlivcVideoEncodeGopEnum.GOP_THREE.getGop()) {
mViewBinding.pushArgsSetting.gopSeekbar.setProgress(LivePushProgressStep.PROGRESS_60);
} else if (videoEncodeGop == AlivcVideoEncodeGopEnum.GOP_FOUR.getGop()) {
mViewBinding.pushArgsSetting.gopSeekbar.setProgress(LivePushProgressStep.PROGRESS_80);
} else if (videoEncodeGop == AlivcVideoEncodeGopEnum.GOP_FIVE.getGop()) {
mViewBinding.pushArgsSetting.gopSeekbar.setProgress(LivePushProgressStep.PROGRESS_100);
}
mViewBinding.pushArgsSetting.fpsText.setText(String.valueOf(alivcLivePushConfig.getFps()));
int configFps = alivcLivePushConfig.getFps();
if (configFps == AlivcFpsEnum.FPS_8.getFps()) {
mViewBinding.pushArgsSetting.fpsSeekbar.setProgress(LivePushProgressStep.PROGRESS_0);
} else if (configFps == AlivcFpsEnum.FPS_10.getFps()) {
mViewBinding.pushArgsSetting.fpsSeekbar.setProgress(LivePushProgressStep.PROGRESS_16);
} else if (configFps == AlivcFpsEnum.FPS_12.getFps()) {
mViewBinding.pushArgsSetting.fpsSeekbar.setProgress(LivePushProgressStep.PROGRESS_33);
} else if (configFps == AlivcFpsEnum.FPS_15.getFps()) {
mViewBinding.pushArgsSetting.fpsSeekbar.setProgress(LivePushProgressStep.PROGRESS_50);
} else if (configFps == AlivcFpsEnum.FPS_20.getFps()) {
mViewBinding.pushArgsSetting.fpsSeekbar.setProgress(LivePushProgressStep.PROGRESS_66);
} else if (configFps == AlivcFpsEnum.FPS_25.getFps()) {
mViewBinding.pushArgsSetting.fpsSeekbar.setProgress(LivePushProgressStep.PROGRESS_80);
} else if (configFps == AlivcFpsEnum.FPS_30.getFps()) {
mViewBinding.pushArgsSetting.fpsSeekbar.setProgress(LivePushProgressStep.PROGRESS_100);
}
AlivcQualityModeEnum configQualityMode = alivcLivePushConfig.getQualityMode();
if (configQualityMode == AlivcQualityModeEnum.QM_RESOLUTION_FIRST) {
mQualityModeDefaultIndex = 0;
mViewBinding.pushArgsSetting.qualityModes.setText(getResources().getString(R.string.quality_resolution_first));
} else if (configQualityMode == AlivcQualityModeEnum.QM_FLUENCY_FIRST) {
mQualityModeDefaultIndex = 1;
mViewBinding.pushArgsSetting.qualityModes.setText(getResources().getString(R.string.quality_fluency_first));
} else if (configQualityMode == AlivcQualityModeEnum.QM_CUSTOM) {
mQualityModeDefaultIndex = 2;
mViewBinding.pushArgsSetting.qualityModes.setText(getResources().getString(R.string.quality_custom));
}
AlivcEncodeType configVideoEncodeType = alivcLivePushConfig.getVideoEncodeType();
if (configVideoEncodeType == AlivcEncodeType.Encode_TYPE_H264) {
mVideoEncoderTypeDefaultIndex = 0;
mViewBinding.pushArgsSetting.videoEncoderType.setText(getResources().getString(R.string.h264));
} else if (configVideoEncodeType == AlivcEncodeType.Encode_TYPE_H265) {
mVideoEncoderTypeDefaultIndex = 1;
mViewBinding.pushArgsSetting.videoEncoderType.setText(getResources().getString(R.string.h265));
}
int bFrames = alivcLivePushConfig.getBFrames();
if (bFrames == 0) {
mBFrameDefaultIndex = 0;
mViewBinding.pushArgsSetting.bFrameNum.setText("0");
} else if (bFrames == 1) {
mBFrameDefaultIndex = 1;
mViewBinding.pushArgsSetting.bFrameNum.setText("1");
} else if (bFrames == 3) {
mBFrameDefaultIndex = 2;
mViewBinding.pushArgsSetting.bFrameNum.setText("3");
}
mViewBinding.pushArgsSetting.hardSwitch.setChecked(alivcLivePushConfig.getVideoEncodeMode() == AlivcEncodeModeEnum.Encode_MODE_HARD);
mViewBinding.pushArgsSetting.audioHardenc.setChecked(alivcLivePushConfig.getAudioEncodeMode() == AlivcEncodeModeEnum.Encode_MODE_HARD);
mViewBinding.pushFunctionSetting.beautyOnSwitch.setChecked(isEnableBeauty);
mViewBinding.pushFunctionSetting.logSwitch.setChecked(isEnableLocalLog);
mViewBinding.pushFunctionSetting.watermarkSwitch.setChecked(isEnableWaterMark);
mViewBinding.pushFunctionSetting.pushMirrorSwitch.setChecked(alivcLivePushConfig.isPushMirror());
mViewBinding.pushFunctionSetting.previewMirrorSwitch.setChecked(alivcLivePushConfig.isPreviewMirror());
mViewBinding.pushFunctionSetting.cameraSwitch.setChecked(alivcLivePushConfig.getCameraType() == AlivcLivePushCameraTypeEnum.CAMERA_TYPE_FRONT.getCameraId());
mViewBinding.pushFunctionSetting.autofocusSwitch.setChecked(alivcLivePushConfig.isAutoFocus());
mViewBinding.pushFunctionSetting.pauseImage.setChecked(!TextUtils.isEmpty(alivcLivePushConfig.getPausePushImage()));
mViewBinding.pushFunctionSetting.networkImage.setChecked(!TextUtils.isEmpty(alivcLivePushConfig.getNetworkPoorPushImage()));
mViewBinding.pushFunctionSetting.musicModeSwitch.setChecked(alivcLivePushConfig.getAudioSceneMode() == AlivcAudioSceneModeEnum.AUDIO_SCENE_MUSIC_MODE);
mViewBinding.pushFunctionSetting.externVideo.setChecked(alivcLivePushConfig.isExternMainStream());
boolean configAudioOnly = alivcLivePushConfig.isAudioOnly();
boolean configVideoOnly = alivcLivePushConfig.isVideoOnly();
if (configAudioOnly && configVideoOnly) {
mPushModeDefaultIndex = 0;
mViewBinding.pushFunctionSetting.pushMode.setText(getResources().getString(R.string.video_push_streaming));
} else {
if (configAudioOnly) {
mPushModeDefaultIndex = 1;
mViewBinding.pushFunctionSetting.pushMode.setText(getResources().getString(R.string.audio_only_push_streaming));
} else {
mPushModeDefaultIndex = 2;
mViewBinding.pushFunctionSetting.pushMode.setText(getResources().getString(R.string.video_only_push_streaming));
}
}
AlivcPreviewDisplayMode configPreviewDisplayMode = alivcLivePushConfig.getPreviewDisplayMode();
if (configPreviewDisplayMode == AlivcPreviewDisplayMode.ALIVC_LIVE_PUSHER_PREVIEW_SCALE_FILL) {
mDisplayModeDefaultIndex = 0;
mViewBinding.pushFunctionSetting.settingDisplayMode.setText(getResources().getString(R.string.display_mode_full));
} else if (configPreviewDisplayMode == AlivcPreviewDisplayMode.ALIVC_LIVE_PUSHER_PREVIEW_ASPECT_FIT) {
mDisplayModeDefaultIndex = 1;
mViewBinding.pushFunctionSetting.settingDisplayMode.setText(getResources().getString(R.string.display_mode_fit));
} else if (configPreviewDisplayMode == AlivcPreviewDisplayMode.ALIVC_LIVE_PUSHER_PREVIEW_ASPECT_FILL) {
mDisplayModeDefaultIndex = 2;
mViewBinding.pushFunctionSetting.settingDisplayMode.setText(getResources().getString(R.string.display_mode_cut));
}
int configPreviewOrientation = alivcLivePushConfig.getPreviewOrientation();
if (configPreviewOrientation == AlivcPreviewOrientationEnum.ORIENTATION_PORTRAIT.getOrientation()) {
mPreviewOrientationDefaultIndex = 0;
mViewBinding.pushFunctionSetting.mainOrientation.setText(getResources().getString(R.string.portrait));
} else if (configPreviewOrientation == AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_LEFT.getOrientation()) {
mPreviewOrientationDefaultIndex = 1;
mViewBinding.pushFunctionSetting.mainOrientation.setText(getResources().getString(R.string.homeLeft));
} else if (configPreviewOrientation == AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_RIGHT.getOrientation()) {
mPreviewOrientationDefaultIndex = 2;
mViewBinding.pushFunctionSetting.mainOrientation.setText(getResources().getString(R.string.homeRight));
}
}
}

View File

@@ -0,0 +1,407 @@
package com.alivc.live.commonui.configview;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.Scroller;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import java.util.List;
public class OptionSelectorView extends View implements Runnable {
private static final String TAG = "OptionSelectorView";
private final float DEFAULT_FRICTION = 0.02f;
private final int DEFAULT_ITEM_HEIGHT = (int) dip2px(39);
private final int DEFAULT_DIVIDER_COLOR = Color.parseColor("#3A3D48");
private final int DEFAULT_VISIBLE_ITEM_NUM = 6;
private final float mHalfShowHeight = DEFAULT_VISIBLE_ITEM_NUM * DEFAULT_ITEM_HEIGHT / 2.0f;
private final int DEFAULT_START_ITEM_COLOR = Color.parseColor("#FCFCFD");
private final int DEFAULT_END_TEXT_COLOR = Color.parseColor("#747A8C");
private final float mMaxTextSize = sp2px(21);
private final float mMinTextSize = sp2px(13);
private List<String> mData;
private int mMinimumVelocity;
private int mMaximumVelocity;
private long mClickTimeout;
private VelocityTracker mVelocityTracker;
private Scroller mScroller;
private int mMinScrollY;
private int mMaxScrollY;
private int mScrollOffsetY;
private boolean mIsDragging = false;
private boolean mIsFlingScroll = false;
private float mTouchY;
private long mTouchDownTime;
private int mCurrentScrollPosition;
private int mSelectedItemPosition = 0;
private OnItemSelectedListener mOnItemSelectedListener;
private Rect mContentRect;
protected Rect mPanelRect;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
public OptionSelectorView(Context context) {
super(context);
initView(context);
}
public OptionSelectorView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public OptionSelectorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context);
}
private void initView(Context context) {
ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
mMinimumVelocity = viewConfiguration.getScaledMinimumFlingVelocity();
mMaximumVelocity = viewConfiguration.getScaledMaximumFlingVelocity();
mClickTimeout = ViewConfiguration.getTapTimeout();
mContentRect = new Rect(0, 0, 0, 0);
mPanelRect = new Rect(0, 0, 0, 0);
mScroller = new Scroller(context);
mScroller.setFriction(DEFAULT_FRICTION);
mMinScrollY = 0;
}
public void setData(List<String> data, int selectPosition) {
mScroller.forceFinished(true);
mData = data;
mCurrentScrollPosition = mSelectedItemPosition = selectPosition;
mScrollOffsetY = DEFAULT_ITEM_HEIGHT * selectPosition;
mMaxScrollY = (mData.size() - 1) * DEFAULT_ITEM_HEIGHT;
invalidateAndCheckItemChange();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
reSetRect();
}
private void reSetRect() {
mContentRect.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
mPanelRect.set(0, 0, getWidth(), getHeight());
}
@Override
public boolean onTouchEvent(MotionEvent event) {
initVelocityTracker(event);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
if (!mScroller.isFinished()) {
mScroller.forceFinished(true);
}
mIsDragging = true;
mTouchY = event.getY();
mTouchDownTime = System.currentTimeMillis();
break;
case MotionEvent.ACTION_MOVE:
float currentY = event.getY();
float dy = currentY - mTouchY;
if (Math.abs(dy) < 1) {
break;
}
scroll((int) -dy);
mTouchY = currentY;
break;
case MotionEvent.ACTION_UP:
mIsDragging = false;
mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
float velocityY = mVelocityTracker.getYVelocity();
if (Math.abs(velocityY) > mMinimumVelocity) {
mScroller.forceFinished(true);
mIsFlingScroll = true;
mScroller.fling(0, mScrollOffsetY, 0, (int) -velocityY, 0, 0,
mMinScrollY, mMaxScrollY);
fixBounceEffect();
} else {
//点击位置和中心点的y轴距离
int clickOffset = 0;
if (System.currentTimeMillis() - mTouchDownTime <= mClickTimeout) {
clickOffset = (int) (event.getY() - getHeight() / 2);
}
int scrollDistance = clickOffset + calculateDistanceNeedToScroll((mScrollOffsetY + clickOffset) % DEFAULT_ITEM_HEIGHT);
if (scrollDistance <= 0) {
scrollDistance = Math.max(scrollDistance, -mScrollOffsetY);
} else {
scrollDistance = Math.min(scrollDistance, mMaxScrollY - mScrollOffsetY);
}
mScroller.startScroll(0, mScrollOffsetY, 0, scrollDistance);
}
invalidateAndCheckItemChange();
ViewCompat.postOnAnimation(this, this);
recycleVelocityTracker();
break;
case MotionEvent.ACTION_CANCEL:
recycleVelocityTracker();
break;
default:
break;
}
return true;
}
@Override
public void run() {
//停止滚动后更新状态
if (mScroller.isFinished() && !mIsDragging && !mIsFlingScroll) {
int currentItemPosition = getCurrentPosition();
if (currentItemPosition == mSelectedItemPosition) {
return;
}
mCurrentScrollPosition = mSelectedItemPosition = currentItemPosition;
//选中监听回调
if (mOnItemSelectedListener != null && mData != null && mData.size() > mSelectedItemPosition) {
mOnItemSelectedListener.onItemSelected(mData.get(mSelectedItemPosition), mSelectedItemPosition);
}
}
if (mScroller.computeScrollOffset()) {
mScrollOffsetY = mScroller.getCurrY();
invalidateAndCheckItemChange();
ViewCompat.postOnAnimation(this, this);
} else if (mIsFlingScroll) {
//快速滚动后调整选中位置到中心
mIsFlingScroll = false;
mScroller.startScroll(0, mScrollOffsetY, 0, calculateDistanceNeedToScroll(mScrollOffsetY % DEFAULT_ITEM_HEIGHT));
invalidateAndCheckItemChange();
ViewCompat.postOnAnimation(this, this);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mContentRect.width() == 0) {
reSetRect();
}
drawDivider(canvas);
//已滚动item数
int scrolledItem;
//没有滚动完的item偏移值
int scrolledOffset;
scrolledItem = mScrollOffsetY / DEFAULT_ITEM_HEIGHT;
scrolledOffset = mScrollOffsetY % DEFAULT_ITEM_HEIGHT;
int half = (DEFAULT_VISIBLE_ITEM_NUM + 1) / 2;
//绘制的第一个选项下标
int firstItemIndex = scrolledItem - half + (mScrollOffsetY > 0 ? 1 : 0);
//绘制的最后一个选项下标
int lastItemIndex = scrolledItem + half;
for (int i = firstItemIndex; i <= lastItemIndex; i++) {
drawItem(canvas, i, scrolledOffset);
}
}
protected void drawItem(Canvas canvas, int index, int scrolledOffset) {
String text = getTextByIndex(index);
if (text == null) {
return;
}
//item 与中间项的偏移
int item2CenterOffsetY;
item2CenterOffsetY = (index - mScrollOffsetY / DEFAULT_ITEM_HEIGHT) * DEFAULT_ITEM_HEIGHT - scrolledOffset;
float textSize = mMaxTextSize - ((mMaxTextSize - mMinTextSize) / mHalfShowHeight * Math.abs(item2CenterOffsetY));
mPaint.setTextSize((float) Math.floor(textSize));
int textColor = evaluate(Math.abs(item2CenterOffsetY) * 1.0f / mHalfShowHeight, DEFAULT_START_ITEM_COLOR, DEFAULT_END_TEXT_COLOR);
mPaint.setColor(textColor);
int textHeightHalf = (int) ((mPaint.getFontMetrics().descent + mPaint.getFontMetrics().ascent) / 2);
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.clipRect(mContentRect);
canvas.drawText(text, mContentRect.centerX(), mContentRect.centerY() + item2CenterOffsetY - textHeightHalf, mPaint);
}
private void drawDivider(Canvas canvas) {
//没有设置分割线颜色时跳过绘制
if (DEFAULT_DIVIDER_COLOR == Color.TRANSPARENT) {
return;
}
mPaint.setColor(DEFAULT_DIVIDER_COLOR);
mPaint.setStrokeWidth(1);
canvas.drawLine(mContentRect.left, mContentRect.centerY() - (DEFAULT_ITEM_HEIGHT >> 1), mContentRect.right, mContentRect.centerY() - (DEFAULT_ITEM_HEIGHT >> 1), mPaint);
canvas.drawLine(mContentRect.left, mContentRect.centerY() + (DEFAULT_ITEM_HEIGHT >> 1), mContentRect.right, mContentRect.centerY() + (DEFAULT_ITEM_HEIGHT >> 1), mPaint);
}
public int evaluate(float fraction, int startValue, int endValue) {
float startA = ((startValue >> 24) & 0xff) / 255.0f;
float startR = ((startValue >> 16) & 0xff) / 255.0f;
float startG = ((startValue >> 8) & 0xff) / 255.0f;
float startB = (startValue & 0xff) / 255.0f;
float endA = ((endValue >> 24) & 0xff) / 255.0f;
float endR = ((endValue >> 16) & 0xff) / 255.0f;
float endG = ((endValue >> 8) & 0xff) / 255.0f;
float endB = (endValue & 0xff) / 255.0f;
// convert from sRGB to linear
startR = (float) Math.pow(startR, 2.2);
startG = (float) Math.pow(startG, 2.2);
startB = (float) Math.pow(startB, 2.2);
endR = (float) Math.pow(endR, 2.2);
endG = (float) Math.pow(endG, 2.2);
endB = (float) Math.pow(endB, 2.2);
// compute the interpolated color in linear space
float a = startA + fraction * (endA - startA);
float r = startR + fraction * (endR - startR);
float g = startG + fraction * (endG - startG);
float b = startB + fraction * (endB - startB);
// convert back to sRGB in the [0..255] range
a = a * 255.0f;
r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;
return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
}
private String getTextByIndex(int index) {
if (mData != null && index >= 0 && index < mData.size()) {
return mData.get(index);
}
return null;
}
private void initVelocityTracker(MotionEvent event) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
}
private void recycleVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
private void scroll(int distance) {
mScrollOffsetY += distance;
mScrollOffsetY = Math.min(mMaxScrollY, Math.max(mMinScrollY, mScrollOffsetY));
invalidateAndCheckItemChange();
}
private void invalidateAndCheckItemChange() {
invalidate();
int currentPosition = getCurrentPosition();
if (mCurrentScrollPosition != currentPosition) {
mCurrentScrollPosition = currentPosition;
}
}
private int getCurrentPosition() {
int itemPosition;
if (mScrollOffsetY < 0) {
itemPosition = (mScrollOffsetY - DEFAULT_ITEM_HEIGHT / 2) / DEFAULT_ITEM_HEIGHT;
} else {
itemPosition = (mScrollOffsetY + DEFAULT_ITEM_HEIGHT / 2) / DEFAULT_ITEM_HEIGHT;
}
int currentPosition = itemPosition % mData.size();
if (currentPosition < 0) {
currentPosition += mData.size();
}
return currentPosition;
}
private int calculateDistanceNeedToScroll(int offset) {
//超过item高度一半需要滚动一个item
if (Math.abs(offset) > DEFAULT_ITEM_HEIGHT / 2) {
if (mScrollOffsetY < 0) {
return -DEFAULT_ITEM_HEIGHT - offset;
} else {
return DEFAULT_ITEM_HEIGHT - offset;
}
}
//当前item回到中心距离
else {
return -offset;
}
}
private void fixBounceEffect() {
//修正快速滑动最后停止位置,没有回弹效果
int stopOffset = mScroller.getFinalY();
int itemScrollOffset = Math.abs(stopOffset % DEFAULT_ITEM_HEIGHT);
//如果滚动偏移超过半个item高度停止位置加一item高度
if (itemScrollOffset > DEFAULT_ITEM_HEIGHT >> 1) {
if (stopOffset < 0) {
stopOffset = stopOffset / DEFAULT_ITEM_HEIGHT * DEFAULT_ITEM_HEIGHT - DEFAULT_ITEM_HEIGHT;
} else {
stopOffset = stopOffset / DEFAULT_ITEM_HEIGHT * DEFAULT_ITEM_HEIGHT + DEFAULT_ITEM_HEIGHT;
}
} else {
stopOffset = stopOffset / DEFAULT_ITEM_HEIGHT * DEFAULT_ITEM_HEIGHT;
}
mScroller.setFinalY(stopOffset);
}
private void draw3DItemText(Canvas canvas, String text, int item2CenterOffsetY, int textHeightHalf) {
canvas.save();
}
public void setOnItemSelectedListener(OnItemSelectedListener onItemSelectedListener) {
mOnItemSelectedListener = onItemSelectedListener;
}
public interface OnItemSelectedListener {
void onItemSelected(String data, int position);
}
/**
* dp宽度转像素值
*/
public float dip2px(float dpValue) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, Resources.getSystem().getDisplayMetrics());
}
/**
* 文字大小转像素值
*/
public float sp2px(float spValue) {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, Resources.getSystem().getDisplayMetrics());
}
}

View File

@@ -0,0 +1,81 @@
package com.alivc.live.commonui.configview;
import android.content.Context;
import android.content.DialogInterface;
import android.view.View;
import android.view.ViewGroup;
import com.alivc.live.commonui.R;
import com.alivc.live.commonui.avdialog.AVLiveBaseBottomSheetDialog;
import com.alivc.live.commonutils.DensityUtil;
import java.util.List;
public class PushConfigBottomSheetLive extends AVLiveBaseBottomSheetDialog {
private OptionSelectorView mOptionSelectorView;
private int mSelectIndex;
private String mTips;
public PushConfigBottomSheetLive(Context context) {
super(context);
}
public PushConfigBottomSheetLive(Context context, int theme) {
super(context, theme);
}
protected PushConfigBottomSheetLive(Context context, boolean cancelable, DialogInterface.OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
}
public void setData(List<String> data, int defaultPosition) {
mSelectIndex = defaultPosition;
mTips = data.get(defaultPosition);
mOptionSelectorView.setData(data, defaultPosition);
}
@Override
protected View getContentView() {
View rootView = getLayoutInflater().inflate(R.layout.push_config_dialog, null, false);
mOptionSelectorView = rootView.findViewById(R.id.optionSelectorView);
mOptionSelectorView.setOnItemSelectedListener(new OptionSelectorView.OnItemSelectedListener() {
@Override
public void onItemSelected(String data, int position) {
mSelectIndex = position;
mTips = data;
}
});
rootView.findViewById(R.id.cancel).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
rootView.findViewById(R.id.confirm_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnPushConfigSelectorListener != null) {
mOnPushConfigSelectorListener.confirm(mTips, mSelectIndex);
}
dismiss();
}
});
return rootView;
}
@Override
protected ViewGroup.LayoutParams getContentLayoutParams() {
return new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, DensityUtil.dip2px(getContext(), 304));
}
private OnPushConfigSelectorListener mOnPushConfigSelectorListener;
public void setOnPushConfigSelectorListener(OnPushConfigSelectorListener onPushConfigSelectorListener) {
mOnPushConfigSelectorListener = onPushConfigSelectorListener;
}
public interface OnPushConfigSelectorListener {
void confirm(String data, int index);
}
}

View File

@@ -0,0 +1,84 @@
package com.alivc.live.commonui.configview;
import android.content.Context;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.alivc.live.commonui.R;
import java.util.Arrays;
import java.util.List;
public class PushConfigDialogImpl {
private PushConfigBottomSheetLive mDialog;
@Nullable
private List<String> getTips(Context context, int id) {
if (id == R.id.quality_modes) {
return Arrays.asList(context.getResources().getString(R.string.quality_resolution_first),
context.getResources().getString(R.string.quality_fluency_first),
context.getResources().getString(R.string.quality_custom));
} else if (id == R.id.audio_channel) {
return Arrays.asList(context.getResources().getString(R.string.single_track), context.getResources().getString(R.string.dual_track));
} else if (id == R.id.audio_profiles) {
return Arrays.asList(context.getResources().getString(R.string.setting_audio_aac_lc),
context.getResources().getString(R.string.setting_audio_aac_he),
context.getResources().getString(R.string.setting_audio_aac_hev2),
context.getResources().getString(R.string.setting_audio_aac_ld));
} else if (id == R.id.video_encoder_type) {
return Arrays.asList(context.getResources().getString(R.string.h264),
context.getResources().getString(R.string.h265));
} else if (id == R.id.b_frame_num) {
return Arrays.asList("0", "1", "3");
} else if (id == R.id.main_orientation) {
return Arrays.asList(context.getResources().getString(R.string.portrait), context.getResources().getString(R.string.homeLeft), context.getResources().getString(R.string.homeRight));
} else if (id == R.id.setting_display_mode) {
return Arrays.asList(context.getResources().getString(R.string.display_mode_full), context.getResources().getString(R.string.display_mode_fit), context.getResources().getString(R.string.display_mode_cut));
} else if (id == R.id.push_mode) {
return Arrays.asList(context.getResources().getString(R.string.video_push_streaming), context.getResources().getString(R.string.audio_only_push_streaming), context.getResources().getString(R.string.video_only_push_streaming));
} else if (id == R.id.setting_camera_capture_output_preference) {
return Arrays.asList(context.getResources().getString(R.string.camera_capture_output_preference_auto), context.getResources().getString(R.string.camera_capture_output_preference_performance), context.getResources().getString(R.string.camera_capture_output_preference_preview));
}
return null;
}
public void showConfigDialog(View view, PushConfigBottomSheetLive.OnPushConfigSelectorListener configSelectorListener, int defaultPosition) {
int selectPosition = defaultPosition;
List<String> tips = getTips(view.getContext(), view.getId());
if (tips == null || defaultPosition >= tips.size()) {
return;
}
if (mDialog == null) {
mDialog = new PushConfigBottomSheetLive(view.getContext(), R.style.Live_BottomSheetDialog);
}
Object lastIndex = view.getTag();
if (lastIndex instanceof Integer) {
selectPosition = (int) lastIndex;
}
mDialog.setData(tips, selectPosition);
mDialog.setOnPushConfigSelectorListener(new PushConfigBottomSheetLive.OnPushConfigSelectorListener() {
@Override
public void confirm(String data, int index) {
view.setTag(index);
if (view instanceof TextView) {
((TextView) view).setText(data);
}
if (configSelectorListener != null) {
configSelectorListener.confirm(data, index);
}
}
});
mDialog.show();
}
public void destroy() {
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
mDialog = null;
}
}
}

View File

@@ -0,0 +1,271 @@
package com.alivc.live.commonui.dialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.text.TextUtils.TruncateAt;
import android.view.View;
import android.view.Window;
import android.view.WindowManager.LayoutParams;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.alivc.live.commonui.R;
import com.alivc.live.commonutils.DensityUtil;
import com.alivc.live.commonutils.TextFormatUtil;
public class CommonDialog extends Dialog {
private TextView titleText;
private TextView tipContent;
private LinearLayout contentLayout;
private FrameLayout contentContainer2;
private ScrollView contentContainer;
private FrameLayout expandContainer;
private View btnTopLine;
private TextView cancelButton;
private View btnLine;
private TextView confirmButton;
private int buttonCount;
public CommonDialog(@NonNull Context context) {
this(context, R.style.DialogStyle);
}
public CommonDialog(@NonNull Context context, int themeResId) {
super(context, themeResId);
this.initView();
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Window window = this.getWindow();
window.setGravity(17);
LayoutParams params = window.getAttributes();
params.width = -1;
params.height = -2;
window.setAttributes(params);
}
public void show() {
if (!this.isShowing()) {
if (this.buttonCount == 0) {
this.btnLine.setVisibility(View.GONE);
this.btnTopLine.setVisibility(View.GONE);
this.setDialogCancelable(true);
} else if (this.buttonCount == 1) {
this.btnLine.setVisibility(View.GONE);
this.btnTopLine.setVisibility(View.VISIBLE);
} else if (this.buttonCount == 2) {
this.btnLine.setVisibility(View.VISIBLE);
this.btnTopLine.setVisibility(View.VISIBLE);
}
super.show();
}
}
public void dismiss() {
if (this.isShowing()) {
super.dismiss();
}
}
private void initView() {
View tipDialogView = this.getLayoutInflater().inflate(R.layout.common_dialog, null);
this.titleText = (TextView) tipDialogView.findViewById(R.id.dialog_title);
this.contentLayout = (LinearLayout) tipDialogView.findViewById(R.id.dialog_content_layout);
this.contentContainer2 = (FrameLayout) tipDialogView.findViewById(R.id.dialog_content_container2);
this.contentContainer = (ScrollView) tipDialogView.findViewById(R.id.dialog_content_container);
this.tipContent = (TextView) tipDialogView.findViewById(R.id.dialog_tip_content);
this.expandContainer = (FrameLayout) tipDialogView.findViewById(R.id.dialog_expand_content_container);
this.btnTopLine = tipDialogView.findViewById(R.id.dialog_btn_top_line);
this.cancelButton = (TextView) tipDialogView.findViewById(R.id.dialog_cancel_btn);
this.btnLine = tipDialogView.findViewById(R.id.dialog_btn_line);
this.confirmButton = (TextView) tipDialogView.findViewById(R.id.dialog_confirm_btn);
View bottomSpace = tipDialogView.findViewById(R.id.dialog_bottom_space);
int space = (int) ((double) DensityUtil.getDisplayMetrics(this.getContext()).heightPixels * 0.05D);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(-1, space);
bottomSpace.setLayoutParams(params);
this.setContentView(tipDialogView);
}
public CommonDialog setDialogCancelable(boolean cancelable) {
this.setCancelable(cancelable);
this.setCanceledOnTouchOutside(cancelable);
return this;
}
public CommonDialog setDialogTitle(int title) {
CharSequence titleStr = title == 0 ? "" : TextFormatUtil.getTextFormat(this.getContext(), title);
return this.setDialogTitle(titleStr);
}
public CommonDialog setDialogTitle(CharSequence title) {
if (this.titleText != null && !TextUtils.isEmpty(title)) {
this.titleText.setText(title);
this.titleText.setVisibility(View.VISIBLE);
}
return this;
}
public CommonDialog setDialogTitleGravity(int gravity) {
if (this.titleText != null) {
this.titleText.setGravity(gravity);
}
return this;
}
public CommonDialog setDialogTitleMaxLines(int lines) {
if (this.titleText != null && lines > 0) {
this.setDialogTitleLinesNoLimit();
this.titleText.setMaxLines(lines);
this.titleText.setEllipsize(TruncateAt.END);
}
return this;
}
public CommonDialog setDialogTitleLinesNoLimit() {
if (this.titleText != null) {
this.titleText.setSingleLine(false);
}
return this;
}
public CommonDialog setDialogContentGravity(int gravity) {
if (this.tipContent != null) {
this.tipContent.setGravity(gravity);
}
return this;
}
public CommonDialog setDialogContent(int tip) {
CharSequence tipStr = tip == 0 ? "" : TextFormatUtil.getTextFormat(this.getContext(), tip);
return this.setDialogContent(tipStr);
}
public CommonDialog setDialogContent(CharSequence tip) {
if (this.tipContent != null && !TextUtils.isEmpty(tip)) {
this.tipContent.setText(tip);
this.contentLayout.setVisibility(View.VISIBLE);
}
return this;
}
public CommonDialog setDialogContentView(View contentView) {
if (this.contentContainer != null && contentView != null) {
this.contentContainer.removeAllViews();
this.contentContainer.addView(contentView);
this.contentLayout.setVisibility(View.VISIBLE);
}
return this;
}
public CommonDialog setDialogContentView2(View contentView) {
if (this.contentContainer2 != null && contentView != null) {
this.contentContainer2.removeAllViews();
this.contentContainer2.addView(contentView);
}
return this;
}
public CommonDialog setExpandView(View expandView) {
if (this.expandContainer != null && expandView != null) {
this.expandContainer.removeAllViews();
this.expandContainer.addView(expandView);
this.expandContainer.setVisibility(View.VISIBLE);
}
return this;
}
public CommonDialog setCancelButton(int cancel, OnClickListener listener) {
return this.setCancelButton(cancel, 0, listener);
}
public CommonDialog setCancelButton(int cancel, int color, OnClickListener listener) {
CharSequence cancelStr = cancel == 0 ? "" : TextFormatUtil.getTextFormat(this.getContext(), cancel);
return this.setCancelButton(cancelStr, color, listener);
}
public CommonDialog setCancelButton(CharSequence cancel, OnClickListener listener) {
this.setCancelButton(cancel, 0, listener);
return this;
}
public CommonDialog setCancelButton(CharSequence cancel, int color, final OnClickListener listener) {
if (this.cancelButton != null && !TextUtils.isEmpty(cancel)) {
this.cancelButton.setText(cancel);
if (color != 0) {
this.cancelButton.setTextColor(color);
}
this.cancelButton.setVisibility(View.VISIBLE);
++this.buttonCount;
if (this.cancelButton != null) {
this.cancelButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
CommonDialog.this.dismiss();
if (listener != null) {
listener.onClick(CommonDialog.this, -2);
}
}
});
}
}
return this;
}
public CommonDialog setConfirmButton(int confirm, OnClickListener listener) {
return this.setConfirmButton(confirm, 0, listener);
}
public CommonDialog setConfirmButton(int confirm, int color, OnClickListener listener) {
CharSequence confirmStr = confirm == 0 ? "" : TextFormatUtil.getTextFormat(this.getContext(), confirm);
return this.setConfirmButton(confirmStr, color, listener);
}
public CommonDialog setConfirmButton(CharSequence confirm, OnClickListener listener) {
this.setConfirmButton(confirm, 0, listener);
return this;
}
public CommonDialog setConfirmButton(CharSequence confirm, int color, final OnClickListener listener) {
if (this.confirmButton != null && !TextUtils.isEmpty(confirm)) {
this.confirmButton.setText(confirm);
if (color != 0) {
this.confirmButton.setTextColor(color);
}
this.confirmButton.setVisibility(View.VISIBLE);
++this.buttonCount;
this.confirmButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
CommonDialog.this.dismiss();
if (listener != null) {
listener.onClick(CommonDialog.this, -1);
}
}
});
}
return this;
}
}

View File

@@ -0,0 +1,71 @@
package com.alivc.live.commonui.dialog;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import com.alivc.live.commonui.R;
import com.alivc.live.commonutils.DensityUtil;
/**
* 设置最大高度的Layout
*/
public class MaxHeightLayout extends LinearLayout {
private float mMaxRatio = 0.75f;
private float mMaxHeight;
private float mMinHeight;
public MaxHeightLayout(Context context) {
super(context);
init();
}
public MaxHeightLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initAtts(context, attrs);
init();
}
public MaxHeightLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAtts(context, attrs);
init();
}
private void initAtts(Context context, AttributeSet attrs) {
TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.mMaxRatio);
if (attributes != null) {
mMaxRatio = attributes.getFloat(R.styleable.mMaxRatio_linear_max_ratio, 0.75f);
attributes.recycle();
}
}
private void init() {
mMaxHeight = mMaxRatio * DensityUtil.getDisplayMetrics(getContext()).heightPixels;
mMinHeight = DensityUtil.dip2px(getContext(), 125);
mMaxHeight = mMaxHeight < mMinHeight ? mMinHeight : mMaxHeight;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (heightMode == MeasureSpec.EXACTLY) {
heightSize = heightSize <= mMaxHeight ? heightSize : (int) mMaxHeight;
}
if (heightMode == MeasureSpec.UNSPECIFIED) {
heightSize = heightSize <= mMaxHeight ? heightSize : (int) mMaxHeight;
}
if (heightMode == MeasureSpec.AT_MOST) {
heightSize = heightSize <= mMaxHeight ? heightSize : (int) mMaxHeight;
}
int maxHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
super.onMeasure(widthMeasureSpec, maxHeightMeasureSpec);
}
}

View File

@@ -0,0 +1,59 @@
package com.alivc.live.commonui.messageview;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.alivc.live.commonui.R;
import java.util.ArrayList;
/**
* 自动追加滚动消息视图适配器
*/
public class AutoScrollMessagesAdapter extends RecyclerView.Adapter<AutoScrollMessagesAdapter.RecyclerViewHolder> {
private @NonNull
ArrayList<String> msgList = new ArrayList<String>();
@NonNull
@Override
public RecyclerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
View view = layoutInflater.inflate(R.layout.layout_callback_message, parent, false);
return new RecyclerViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position) {
holder.applyTextMsg(msgList.get(position));
}
@Override
public int getItemCount() {
return msgList.size();
}
public void applyDataMessage(String msg) {
msgList.add(msg);
notifyDataSetChanged();
}
static class RecyclerViewHolder extends RecyclerView.ViewHolder {
private TextView mTextView;
RecyclerViewHolder(View itemView) {
super(itemView);
mTextView = itemView.findViewById(R.id.text_msg);
}
public void applyTextMsg(String msg) {
if (mTextView != null) {
mTextView.setText(msg);
}
}
}
}

View File

@@ -0,0 +1,44 @@
package com.alivc.live.commonui.messageview;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* 自动追加滚动消息视图
*/
public class AutoScrollMessagesView extends RecyclerView {
private final AutoScrollMessagesAdapter mAdapter = new AutoScrollMessagesAdapter();
public AutoScrollMessagesView(@NonNull Context context) {
this(context, null, 0);
}
public AutoScrollMessagesView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public AutoScrollMessagesView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initData();
}
private void initData() {
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
setLayoutManager(linearLayoutManager);
setAdapter(mAdapter);
setDrawingCacheEnabled(true);
setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
}
public void appendMessage(String msg) {
mAdapter.applyDataMessage(msg);
smoothScrollToPosition(mAdapter.getItemCount());
}
}

View File

@@ -0,0 +1,81 @@
package com.alivc.live.commonui.seiview;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import com.alivc.live.commonui.R;
import java.util.regex.Pattern;
public class LivePusherSEIView extends LinearLayout {
private static final int DEFAULT_PAYLOAD_TYPE = 5;
private static final String DEFAULT_SEI_MESSAGE = "010203=-./,_adcd-&^%$~@";
private EditText mPayloadEditText;
private EditText mSeiEditText;
private Button mSendSEIButton;
private SendSeiViewListener mListener;
public LivePusherSEIView(Context context) {
super(context);
init(context);
}
public LivePusherSEIView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public LivePusherSEIView(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.push_sei_view_layout, this, true);
mPayloadEditText = inflateView.findViewById(R.id.et_sei_payload_type);
mPayloadEditText.setText(String.valueOf(DEFAULT_PAYLOAD_TYPE));
mSeiEditText = inflateView.findViewById(R.id.et_sei);
mSeiEditText.setText(DEFAULT_SEI_MESSAGE);
mSendSEIButton = inflateView.findViewById(R.id.btn_send_sei);
mSendSEIButton.setOnClickListener(view -> {
if (mListener != null) {
// 如果输入框为空默认输出指定的json格式以供测试
String text = mSeiEditText.getText().toString();
if (TextUtils.isEmpty(text)) {
text = DEFAULT_SEI_MESSAGE;
}
// 如果输入框为空默认payload type=5以供测试
String payloadEdit = mPayloadEditText.getText().toString();
int payload = DEFAULT_PAYLOAD_TYPE;
Pattern pattern = Pattern.compile("[0-9]*");
if (pattern.matcher(payloadEdit).matches()) {
payload = Integer.parseInt(payloadEdit);
}
mListener.onSendSeiClick(payload, text);
}
});
}
public interface SendSeiViewListener {
void onSendSeiClick(int payload, String text);
}
public void setSendSeiViewListener(SendSeiViewListener mListener) {
this.mListener = mListener;
}
}

View File

@@ -0,0 +1,186 @@
package com.alivc.live.commonutils;
import android.content.Context;
import android.graphics.Point;
import android.os.Build;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import java.lang.reflect.Field;
import java.math.BigDecimal;
public class DensityUtil {
private static final String TAG = DensityUtil.class.getSimpleName();
/**
* dp、sp 转换为 px 的工具类
*
* @author fxsky 2012.11.12
*
*/
/**
* 将px值转换为dip或dp值保证尺寸大小不变
*
* @param pxValue
* @param scale DisplayMetrics类中属性density
* @return
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
/**
* 将dip或dp值转换为px值保证尺寸大小不变
*
* @param dipValue
* @param scale DisplayMetrics类中属性density
* @return
*/
public static int dip2px(Context context, float dipValue) {
if (context == null || context.getResources() == null)
return 1;
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
/**
* 将px值转换为sp值保证文字大小不变
*
* @param pxValue
* @param fontScale DisplayMetrics类中属性scaledDensity
* @return
*/
public static int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
/**
* 将sp值转换为px值保证文字大小不变
*
* @param spValue
* @param fontScale DisplayMetrics类中属性scaledDensity
* @return
*/
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
private static DisplayMetrics sDisplayMetrics;
private static int sStatusBarHeight = -1;
public static DisplayMetrics getDisplayMetrics(Context context) {
if (sDisplayMetrics == null) {
sDisplayMetrics = context.getResources().getDisplayMetrics();
}
return sDisplayMetrics;
}
public static int getStatusBarHeight(Context context) {
if (sStatusBarHeight == -1) {
try {
Class<?> c = Class.forName("com.android.internal.R$dimen");
Object o = c.newInstance();
Field field = c.getField("status_bar_height");
int x = (Integer) field.get(o);
sStatusBarHeight = context.getResources().getDimensionPixelSize(x);
} catch (ClassNotFoundException e) {
Log.e(TAG, e.getMessage());
} catch (InstantiationException e) {
Log.e(TAG, e.getMessage());
} catch (IllegalAccessException e) {
Log.e(TAG, e.getMessage());
} catch (NoSuchFieldException e) {
Log.e(TAG, e.getMessage());
}
}
return sStatusBarHeight;
}
/**
* 获取view的宽度
*/
public static int getViewWidth(View view) {
view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
return view.getMeasuredWidth();
}
/**
* 获取view的高度
*/
public static int getViewHeight(View view) {
view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
return view.getMeasuredHeight();
}
/**
* 获取适配后的高度
*
* @return
*/
public static int getScaleHeight(Context context, int srcHeight) {
double realHeight = getScreenRealHeight(context);
if (realHeight <= 0) {
return srcHeight;
}
if (realHeight >= 37) {// [37, +∞)
srcHeight = (int) (srcHeight * 0.9 * 0.9 * 0.9);
} else if (realHeight >= 33) {// [33, 37)
srcHeight = (int) (srcHeight * 0.9 * 0.9);
} else if (realHeight >= 27) {// [27, 33)
srcHeight = (int) (srcHeight * 0.9);
}
// [0, 27]
return srcHeight;
}
/**
* 获取当前手机屏幕的尺寸(单位:像素)
*/
public static double getScreenRealHeight(Context context) {
try {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Point point = new Point();
manager.getDefaultDisplay().getRealSize(point);
DisplayMetrics dm = context.getResources().getDisplayMetrics();
// dm.ydpi是屏幕y方向的真实密度值
return new BigDecimal(Math.pow(point.y / dm.ydpi, 2)).setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
}
} catch (Exception e) {
Log.d("DensityUtil", e.getMessage());
}
return 0;
}
/**
* 获取当前手机屏幕的尺寸(单位:像素)
*/
public static double getScreenSize(Context context) {
try {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Point point = new Point();
manager.getDefaultDisplay().getRealSize(point);
DisplayMetrics dm = context.getResources().getDisplayMetrics();
// dm.xdpi是屏幕x方向的真实密度值
double x = Math.pow(point.x / dm.xdpi, 2);
// dm.ydpi是屏幕y方向的真实密度值
double y = Math.pow(point.y / dm.ydpi, 2);
return new BigDecimal(Math.sqrt(x + y)).setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
}
} catch (Exception e) {
Log.d("DensityUtil", e.getMessage());
}
return 0;
}
}

View File

@@ -0,0 +1,86 @@
package com.alivc.live.commonui.utils;
import android.annotation.TargetApi;
import android.app.Activity;
import android.graphics.Color;
import android.os.Build;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.ColorInt;
import androidx.core.view.ViewCompat;
public class StatusBarUtil {
/**
* 沉浸式状态栏。
* 支持 4.4 以上版本的 MIUI 和 Flyme以及 5.0 以上版本的其他 Android。
*
* @param activity 需要被设置沉浸式状态栏的 Activity。
*/
public static void translucent(Activity activity, @ColorInt int colorOn5x) {
try {
Window window = activity.getWindow();
translucent(window, colorOn5x);
}catch (Exception e){
e.printStackTrace();
}
}
@TargetApi(19)
private static void translucent(Window window, @ColorInt int colorOn5x) {
if (isNotchOfficialSupport()) {
handleDisplayCutoutMode(window);
}
int systemUiVisibility = window.getDecorView().getSystemUiVisibility();
systemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
window.getDecorView().setSystemUiVisibility(systemUiVisibility);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// android 6以后可以改状态栏字体颜色因此可以自行设置为透明
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
}
}
@TargetApi(28)
private static void handleDisplayCutoutMode(final Window window) {
View decorView = window.getDecorView();
if (decorView != null) {
if (ViewCompat.isAttachedToWindow(decorView)) {
realHandleDisplayCutoutMode(window, decorView);
} else {
decorView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
v.removeOnAttachStateChangeListener(this);
realHandleDisplayCutoutMode(window, v);
}
@Override
public void onViewDetachedFromWindow(View v) {
}
});
}
}
}
@TargetApi(28)
private static void realHandleDisplayCutoutMode(Window window, View decorView) {
if (decorView.getRootWindowInsets() != null &&
decorView.getRootWindowInsets().getDisplayCutout() != null) {
WindowManager.LayoutParams params = window.getAttributes();
params.layoutInDisplayCutoutMode = WindowManager.LayoutParams
.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
window.setAttributes(params);
}
}
public static boolean isNotchOfficialSupport(){
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
}
}

View File

@@ -0,0 +1,108 @@
package com.alivc.live.commonui.widgets;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.widget.ImageView;
import android.widget.TextView;
import com.alivc.live.commonui.R;
public class AVLiveLoadingDialog extends Dialog {
private Context context;
private ImageView mIconView;
private TextView mTipView;
private CharSequence mTipMsg;
public AVLiveLoadingDialog(Context context) {
super(context);
this.context = context;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init();
}
private void init() {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.av_live_loading_dialog_layout, null, false);
mIconView = view.findViewById(R.id.av_loading_dialog_icon);
mTipView = view.findViewById(R.id.av_loading_dialog_tip);
if (mTipMsg != null) {
mTipView.setVisibility(View.VISIBLE);
mTipView.setText(mTipMsg);
} else {
mTipView.setVisibility(View.GONE);
}
setContentView(view);
Window dialogWindow = getWindow();
dialogWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
dialogWindow.setBackgroundDrawableResource(android.R.color.transparent);
WindowManager.LayoutParams lp = dialogWindow.getAttributes();
lp.alpha = 1.0f;
lp.dimAmount = 0.0f;
lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
lp.verticalMargin = 0.3f;
dialogWindow.setAttributes(lp);
setCancelable(false);
setCanceledOnTouchOutside(false);
startAnimation();
}
private void startAnimation() {
if (mIconView == null || mIconView.getAnimation() != null) {
return;
}
RotateAnimation rotateAnimation = new RotateAnimation(0f, 360f, RotateAnimation.RELATIVE_TO_SELF,
0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
//设置动画持续时长
rotateAnimation.setDuration(1000);
rotateAnimation.setInterpolator(new LinearInterpolator());
//设置动画的重复模式反转REVERSE和重新开始RESTART
rotateAnimation.setRepeatMode(ScaleAnimation.RESTART);
//设置动画播放次数
rotateAnimation.setRepeatCount(ScaleAnimation.INFINITE);
//开始动画
mIconView.startAnimation(rotateAnimation);
}
private void stopAnimation() {
if (mIconView == null || mIconView.getAnimation() == null) {
return;
}
mIconView.clearAnimation();
}
public AVLiveLoadingDialog tip(CharSequence tip) {
mTipMsg = tip;
return this;
}
@Override
public void show() {
super.show();
startAnimation();
}
@Override
public void dismiss() {
stopAnimation();
super.dismiss();
}
}

View File

@@ -0,0 +1,106 @@
package com.alivc.live.commonui.widgets;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.Gravity;
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.TextView;
import androidx.annotation.Nullable;
/**
* @author keria
* @date 2024/5/23
* @brief 带文字的按钮开关
*/
public class LivePushTextSwitch extends LinearLayout {
private TextView textView;
private Switch switchView;
/**
* 定义回调接口,用于监听 Switch 状态变化
*/
public interface OnSwitchToggleListener {
void onSwitchToggled(boolean isChecked);
}
private OnSwitchToggleListener onSwitchToggleListener;
public LivePushTextSwitch(Context context) {
super(context);
init(context);
}
public LivePushTextSwitch(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public LivePushTextSwitch(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
// 设置横向布局
setOrientation(HORIZONTAL);
setGravity(Gravity.CENTER_VERTICAL);
textView = new TextView(context);
textView.setTextColor(Color.WHITE);
textView.setLines(1);
textView.setTextSize(12);
textView.setPadding(0, 0, 12, 0);
switchView = new Switch(context);
switchView.setChecked(false);
// 设置 TextView 的布局参数,使其宽度包裹内容并且靠左对齐
LayoutParams textViewLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
textViewLayoutParams.gravity = Gravity.START | Gravity.CENTER_VERTICAL;
// 设置 Switch 的布局参数,使其宽度包裹内容并且靠右对齐
LayoutParams switchLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
switchLayoutParams.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
textView.setLayoutParams(textViewLayoutParams);
switchView.setLayoutParams(switchLayoutParams);
// 将 TextView 和 Switch 添加到布局中
addView(textView);
addView(switchView);
// 设置 Switch 的监听事件
switchView.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (onSwitchToggleListener != null) {
onSwitchToggleListener.onSwitchToggled(isChecked);
}
});
setPadding(0, 3, 3, 3);
}
/**
* 用于设置 TextView 的文本
*/
public void setTextViewText(String text) {
textView.setText(text);
}
/**
* 用于设置 Switch 的初始状态
*/
public void setSwitchChecked(boolean checked) {
switchView.setChecked(checked);
}
/**
* 用于设置回调监听器
*/
public void setOnSwitchToggleListener(OnSwitchToggleListener listener) {
this.onSwitchToggleListener = listener;
}
}

View File

@@ -0,0 +1,164 @@
package com.alivc.live.commonui.widgets;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Switch;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import com.alivc.live.commonui.R;
import com.alivc.live.commonutils.FileUtil;
import com.alivc.live.pusher.WaterMarkInfo;
import java.util.ArrayList;
public class PushWaterMarkDialog extends DialogFragment {
private Switch mSwitch;
private EditText mX;
private EditText mY;
private EditText mW;
private Switch mSwitch1;
private EditText mX1;
private EditText mY1;
private EditText mW1;
private Switch mSwitch2;
private EditText mX2;
private EditText mY2;
private EditText mW2;
private ArrayList<WaterMarkInfo> mWaterMarkInfos;
private WaterMarkInfo mWaterMarkInfo = new WaterMarkInfo();
private WaterMarkInfo mWaterMarkInfo1 = new WaterMarkInfo();
private WaterMarkInfo mWaterMarkInfo2 = new WaterMarkInfo();
private boolean mWater = false;
private boolean mWater1 = false;
private boolean mWater2 = false;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getDialog().getWindow().getAttributes().windowAnimations = R.style.dialog_animation;
getDialog().setCanceledOnTouchOutside(true);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
View view = inflater.inflate(R.layout.push_watermark, container);
mX = (EditText) view.findViewById(R.id.x);
mY = (EditText) view.findViewById(R.id.y);
mW = (EditText) view.findViewById(R.id.w);
mX1 = (EditText) view.findViewById(R.id.x1);
mY1 = (EditText) view.findViewById(R.id.y1);
mW1 = (EditText) view.findViewById(R.id.w1);
mX2 = (EditText) view.findViewById(R.id.x2);
mY2 = (EditText) view.findViewById(R.id.y2);
mW2 = (EditText) view.findViewById(R.id.w2);
mSwitch = (Switch) view.findViewById(R.id.watermark);
mSwitch1 = (Switch) view.findViewById(R.id.watermark1);
mSwitch2 = (Switch) view.findViewById(R.id.watermark2);
mSwitch.setOnCheckedChangeListener(onCheckedChangeListener);
mSwitch1.setOnCheckedChangeListener(onCheckedChangeListener);
mSwitch2.setOnCheckedChangeListener(onCheckedChangeListener);
return view;
}
@Override
public void onResume() {
getDialog().getWindow().setGravity(Gravity.BOTTOM);
super.onResume();
DisplayMetrics dpMetrics = new DisplayMetrics();
getActivity().getWindow().getWindowManager().getDefaultDisplay().getMetrics(dpMetrics);
WindowManager.LayoutParams p = getDialog().getWindow().getAttributes();
p.width = dpMetrics.widthPixels;
p.height = dpMetrics.heightPixels * 2 / 3;
getDialog().getWindow().setAttributes(p);
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
if (mWaterMarkInfos == null) {
return;
}
try {
if (!mX.getText().toString().isEmpty() && !mY.getText().toString().isEmpty() && !mW.getText().toString().isEmpty()) {
mWaterMarkInfo.mWaterMarkCoordX = Float.valueOf(mX.getText().toString());
mWaterMarkInfo.mWaterMarkCoordY = Float.valueOf(mY.getText().toString());
mWaterMarkInfo.mWaterMarkWidth = Float.valueOf(mW.getText().toString());
mWaterMarkInfo.mWaterMarkPath = FileUtil.combinePaths(FileUtil.getInternalFilesFolder(getContext()), "alivc_resource/watermark.png");
mWater = true;
}
if (!mX1.getText().toString().isEmpty() && !mY1.getText().toString().isEmpty() && !mW1.getText().toString().isEmpty()) {
mWaterMarkInfo1.mWaterMarkCoordX = Float.valueOf(mX1.getText().toString());
mWaterMarkInfo1.mWaterMarkCoordY = Float.valueOf(mY1.getText().toString());
mWaterMarkInfo1.mWaterMarkWidth = Float.valueOf(mW1.getText().toString());
mWaterMarkInfo1.mWaterMarkPath = FileUtil.combinePaths(FileUtil.getInternalFilesFolder(getContext()), "alivc_resource/watermark.png");
mWater1 = true;
}
if (!mX2.getText().toString().isEmpty() && !mY2.getText().toString().isEmpty() && !mW2.getText().toString().isEmpty()) {
mWaterMarkInfo2.mWaterMarkCoordX = Float.valueOf(mX2.getText().toString());
mWaterMarkInfo2.mWaterMarkCoordY = Float.valueOf(mY2.getText().toString());
mWaterMarkInfo2.mWaterMarkWidth = Float.valueOf(mW2.getText().toString());
mWaterMarkInfo2.mWaterMarkPath = FileUtil.combinePaths(FileUtil.getInternalFilesFolder(getContext()), "alivc_resource/watermark.png");
mWater2 = true;
}
} catch (NumberFormatException e) {
e.printStackTrace();
}
if (mWaterMarkInfos.size() > 0) {
mWaterMarkInfos.clear();
}
if (mWater) {
mWaterMarkInfos.add(mWaterMarkInfo);
}
if (mWater1) {
mWaterMarkInfos.add(mWaterMarkInfo1);
}
if (mWater2) {
mWaterMarkInfos.add(mWaterMarkInfo2);
}
}
public void setWaterMarkInfo(ArrayList<WaterMarkInfo> waterMarkInfos) {
this.mWaterMarkInfos = waterMarkInfos;
}
private final CompoundButton.OnCheckedChangeListener onCheckedChangeListener = new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
int id = compoundButton.getId();
if (id == R.id.watermark) {
mWater = b;
} else if (id == R.id.watermark1) {
mWater1 = b;
} else if (id == R.id.watermark2) {
mWater2 = b;
}
}
};
}

View File

@@ -0,0 +1,149 @@
package com.alivc.live.commonui.widgets;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
import java.util.Random;
/**
* @author keria
* @date 2024/7/9
* @brief 可缩放的FrameLayout
* @note 备注该FrameLayout容器仅用于API测试测试在不同FrameLayout切换策略下的表现及性能
* @note 如果您对接直播连麦可直接使用FrameLayout容器作为预览和拉流的渲染窗口
*/
public class ResizableFrameLayout extends FrameLayout {
private static final int ANIMATION_DURATION = 200;
private int originalWidth;
private int originalHeight;
private boolean isResized = false;
private static final ResizeStrategy[] strategies = new ResizeStrategy[]{new HalveStrategy(), new EqualStrategy()};
private ResizeStrategy currentStrategy;
private final Random random = new Random();
public ResizableFrameLayout(Context context) {
super(context);
initializeDefaultStrategy();
}
public ResizableFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initializeDefaultStrategy();
}
public ResizableFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeDefaultStrategy();
}
private void initializeDefaultStrategy() {
currentStrategy = new HalveStrategy(); // 默认为缩放策略
}
public void resize() {
useRandomStrategy();
toggleSize();
}
public void resize(ResizeStrategy strategy) {
setResizeStrategy(strategy);
toggleSize();
}
private void toggleSize() {
// 保存原始宽高
if (originalWidth == 0 || originalHeight == 0) {
originalWidth = getWidth();
originalHeight = getHeight();
}
// 根据策略计算目标宽高
int targetWidth = isResized ? originalWidth : currentStrategy.getTargetWidth(originalWidth, originalHeight);
int targetHeight = isResized ? originalHeight : currentStrategy.getTargetHeight(originalWidth, originalHeight);
// 创建动画
animateResize(targetWidth, targetHeight);
// 切换标志位
isResized = !isResized;
}
private void animateResize(int targetWidth, int targetHeight) {
ValueAnimator widthAnimator = ValueAnimator.ofInt(getWidth(), targetWidth);
ValueAnimator heightAnimator = ValueAnimator.ofInt(getHeight(), targetHeight);
widthAnimator.addUpdateListener(animation -> {
ViewGroup.LayoutParams layoutParams = getLayoutParams();
layoutParams.width = (int) animation.getAnimatedValue();
setLayoutParams(layoutParams);
});
heightAnimator.addUpdateListener(animation -> {
ViewGroup.LayoutParams layoutParams = getLayoutParams();
layoutParams.height = (int) animation.getAnimatedValue();
setLayoutParams(layoutParams);
});
widthAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
heightAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
widthAnimator.setDuration(ANIMATION_DURATION);
heightAnimator.setDuration(ANIMATION_DURATION);
widthAnimator.start();
heightAnimator.start();
}
public void setResizeStrategy(ResizeStrategy strategy) {
this.currentStrategy = strategy;
}
public void useRandomStrategy() {
int randomIndex = random.nextInt(strategies.length);
setResizeStrategy(strategies[randomIndex]);
}
/**
* 缩放策略宽高除以2策略
*/
public static class HalveStrategy implements ResizeStrategy {
@Override
public int getTargetWidth(int originalWidth, int originalHeight) {
return originalWidth / 2;
}
@Override
public int getTargetHeight(int originalWidth, int originalHeight) {
return originalHeight / 2;
}
}
/**
* 正方形策略(宽高相等)
*/
public static class EqualStrategy implements ResizeStrategy {
@Override
public int getTargetWidth(int originalWidth, int originalHeight) {
return Math.min(originalWidth, originalHeight);
}
@Override
public int getTargetHeight(int originalWidth, int originalHeight) {
return Math.min(originalWidth, originalHeight);
}
}
public interface ResizeStrategy {
int getTargetWidth(int originalWidth, int originalHeight);
int getTargetHeight(int originalWidth, int originalHeight);
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fillAfter="true"
android:fromXDelta="0.0"
android:fromYDelta="0.0"
android:toXDelta="0.0"
android:toYDelta="100.0%p" />
</set>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="256"
android:fillAfter="true"
android:fromXDelta="0.0"
android:fromYDelta="100.0%"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toXDelta="0.0"
android:toYDelta="0.0" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 B

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_selected="true" android:drawable="@drawable/beauty_on"/>
<item android:state_selected="false" android:drawable="@drawable/beauty_on"/>
</selector>

View File

@@ -0,0 +1,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="2dp" />
<solid android:color="#1C1D22" />
</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">
<corners android:radius="12dp" />
<solid android:color="@color/bg_weak" />
<stroke android:width="1dp" android:color="#3A3D48" />
</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">
<corners android:radius="12dp" />
<solid android:color="#23262F" />
<stroke android:width="1dp" android:color="#3A3D48" />
</shape>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/live_list_item_bg" android:state_pressed="true"/>
<item android:drawable="@drawable/live_list_item_bg" android:state_hovered="true"/>
<item android:drawable="@drawable/live_list_item_bg" android:state_selected="true"/>
<item android:drawable="@drawable/live_list_item_fg" />
</selector>

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">
<corners android:radius="8dp" />
<solid android:color="#1C1D22" />
</shape>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 背景 gradient是渐变,corners定义的是圆角 -->
<item android:id="@android:id/background">
<shape>
<solid android:color="#3A3D48" />
</shape>
</item>
<!-- 进度条 -->
<item android:id="@android:id/progress">
<clip>
<shape>
<solid android:color="#4DCFE1" />
</shape>
</clip>
</item>
</layer-list>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 背景 gradient是渐变,corners定义的是圆角 -->
<item
android:id="@android:id/background"
android:gravity="center_vertical|fill_horizontal">
<shape>
<size android:height="2dp" />
<solid android:color="#3A3D48" />
</shape>
</item>
<!-- 进度条 -->
<item
android:id="@android:id/progress"
android:gravity="center_vertical|fill_horizontal">
<clip>
<shape>
<size android:height="2dp" />
<solid android:color="#4DCFE1" />
</shape>
</clip>
</item>
</layer-list>

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">
<stroke android:width="1dp" android:color="@color/darker_gray"/>
<solid android:color="@android:color/white" />
<corners android:radius="10dp" />
</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="@color/transparent"/>
<stroke android:width="2dp"
android:color="@color/des_txt_color"/>
</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="@color/transparent" />
<stroke android:width="2dp"
android:color="@color/text_true_color"/>
</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="@color/des_txt_color"/>
</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="@color/text_true_color" />
</shape>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="44dp">
<ImageView
android:id="@+id/aui_player_actionbar_left_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp"
android:visibility="gone" />
<TextView
android:id="@+id/aui_player_actionbar_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:textColor="#FCFCFD"
android:textFontWeight="500"
android:textSize="16sp" />
<ImageView
android:id="@+id/aui_player_actionbar_right_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="20dp"
android:visibility="gone" />
</RelativeLayout>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#1C1D22"
android:fitsSystemWindows="true"
android:orientation="vertical">
<com.alivc.live.commonui.activity.AUILiveActionBar
android:id="@+id/aui_player_base_title"
style="@style/LiveBaseActionBar_Style"
tools:leftImageSrc="@drawable/ic_live_action_bar_back"
tools:rightImageSrc="@drawable/ic_live_actionbar_more"
tools:showLeftView="false"
tools:showRightView="false"
tools:showTitle="true" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/aui_player_base_main_recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="72dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="12dp"
android:layout_marginRight="20dp"
android:background="@drawable/live_list_item_selector">
<ImageView
android:id="@+id/av_list_item_image"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="20dp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="16dp"
android:layout_toRightOf="@+id/av_list_item_image"
android:orientation="vertical">
<TextView
android:id="@+id/av_list_item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lineHeight="24dp"
android:textColor="#FCFCFD"
android:textFontWeight="500"
android:textSize="16sp" />
<TextView
android:id="@+id/av_list_item_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="30dp"
android:ellipsize="end"
android:lineHeight="18dp"
android:maxLines="1"
android:textColor="#B2B7C4"
android:textFontWeight="500"
android:textSize="12sp" />
</LinearLayout>
<ImageView
android:id="@+id/av_list_item_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="20dp"
android:src="@drawable/ic_live_arrow_right" />
</RelativeLayout>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<View
android:id="@+id/touch_outside"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:soundEffectsEnabled="false"
tools:ignore="UnusedAttribute" />
<FrameLayout
android:id="@+id/bottom_sheet_container"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</FrameLayout>
<FrameLayout
android:id="@+id/bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</FrameLayout>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00000000">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="@drawable/live_loading_dialog_bg"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingLeft="14dp"
android:paddingTop="12dp"
android:paddingRight="14dp"
android:paddingBottom="12dp">
<ImageView
android:id="@+id/av_loading_dialog_icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:src="@drawable/av_live_loading_dialog_icon" />
<TextView
android:id="@+id/av_loading_dialog_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:ellipsize="middle"
android:gravity="center"
android:maxWidth="198dp"
android:maxLines="2"
android:minWidth="112dp"
android:textColor="@color/text_strong"
android:textSize="14sp" />
</LinearLayout>
</FrameLayout>

View File

@@ -0,0 +1,158 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:background="#80000000">
<com.alivc.live.commonui.dialog.MaxHeightLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="50dp"
android:layout_marginRight="50dp"
android:layout_weight="1"
android:background="@drawable/live_dialog_bg"
android:minHeight="125dp"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="20dp" />
<TextView
android:id="@+id/dialog_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:includeFontPadding="false"
android:singleLine="true"
android:textColor="#FCFCFD"
android:textSize="18sp"
android:visibility="gone"
tools:text="18dp大标题"
tools:visibility="visible" />
<LinearLayout
android:id="@+id/dialog_content_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="30dp"
android:layout_marginTop="9dp"
android:layout_marginRight="30dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<FrameLayout
android:id="@+id/dialog_content_container2"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="@+id/dialog_content_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never"
android:scrollbars="none">
<!-- 解决内部滑动问题 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/dialog_tip_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:includeFontPadding="false"
android:lineSpacingExtra="3dp"
android:textColor="#FCFCFD"
android:textSize="14sp"
tools:text="14dp提示内容" />
</LinearLayout>
</ScrollView>
</FrameLayout>
<FrameLayout
android:id="@+id/dialog_expand_content_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
</LinearLayout>
<View
android:id="@+id/dialog_btn_top_line"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginTop="30dp"
android:background="#3A3D48"
android:visibility="gone"
tools:visibility="visible" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/dialog_cancel_btn"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:gravity="center"
android:includeFontPadding="false"
android:singleLine="true"
android:text="取消"
android:textColor="#4DCFE1"
android:textSize="18sp"
android:visibility="gone"
tools:visibility="visible" />
<View
android:id="@+id/dialog_btn_line"
android:layout_width="0.5dp"
android:layout_height="52dp"
android:background="#3A3D48"
android:visibility="gone"
tools:visibility="visible" />
<TextView
android:id="@+id/dialog_confirm_btn"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_weight="1"
android:gravity="center"
android:includeFontPadding="false"
android:singleLine="true"
android:text="确定"
android:textColor="#4DCFE1"
android:textSize="18sp"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
</LinearLayout>
<!-- 解决弹窗中心点与屏幕高度45%的位置重合 -->
<View
android:id="@+id/dialog_bottom_space"
android:layout_width="match_parent"
android:layout_height="30dp" />
</com.alivc.live.commonui.dialog.MaxHeightLayout>
</FrameLayout>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/text_msg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/color_text_white"
android:textSize="12dp" />
</FrameLayout>

View File

@@ -0,0 +1,804 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="39dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:background="#141416"
android:gravity="center_vertical"
android:text="@string/resolution_label"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_24px" />
<com.alivc.live.commonui.configview.LivePushResolutionView
android:id="@+id/resolution_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:layout_height="@dimen/view_height_size_45">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/bitrate_control"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/bitrate_control"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="true"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:layout_height="@dimen/view_height_size_45"
android:gravity="center_vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/variable_resolution"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/variable_resolution"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:minHeight="@dimen/view_height_size_45"
android:text="@string/resolution_label_desc"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_24px" />
<View
android:layout_width="match_parent"
android:layout_height="39dp"
android:background="#141416" />
<FrameLayout
android:layout_width="match_parent"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:layout_height="@dimen/view_height_size_45"
android:gravity="center_vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/advance_config"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/advance_config"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<LinearLayout
android:id="@+id/advance_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/push_mode"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<TextView
android:id="@+id/quality_modes"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical|end"
android:paddingEnd="23dp"
android:text="@string/quality_resolution_first"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_live_arrow_right" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_marginStart="20dp"
android:layout_height="1px"
android:background="#3A3D48" />
<TextView
android:background="#141416"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:minHeight="@dimen/view_height_size_45"
android:text="@string/video_label_desc"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_24px" />
<FrameLayout
android:layout_width="match_parent"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:layout_height="@dimen/view_height_size_45">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/target_bitrate"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<EditText
android:id="@+id/target_rate_edit"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="28dp"
android:background="@null"
android:hint="@string/target_rate_value"
android:inputType="number"
android:textColor="#747A8C"
android:textColorHint="#747A8C"
android:maxLength="9"
android:paddingLeft="10dp"
android:textSize="@dimen/font_size_28px" />
<TextView
android:id="@+id/kb_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:text="/kbps"
android:textColor="#747A8C"
android:textSize="11sp" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:layout_height="@dimen/view_height_size_45">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/min_bitrate"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<EditText
android:id="@+id/min_rate_edit"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="28dp"
android:background="@null"
android:hint="@string/min_rate_value"
android:inputType="number"
android:textColor="#747A8C"
android:textColorHint="#747A8C"
android:maxLength="9"
android:paddingLeft="10dp"
android:textSize="@dimen/font_size_28px" />
<TextView
android:id="@+id/min_kb_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:text="/kbps"
android:textColor="#747A8C"
android:textSize="11sp" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:layout_height="@dimen/view_height_size_45">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/initial_bitrate"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<EditText
android:id="@+id/init_rate_edit"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="28dp"
android:background="@null"
android:hint="@string/initial_bit_value"
android:inputType="number"
android:textColor="#747A8C"
android:textColorHint="#747A8C"
android:maxLength="9"
android:paddingLeft="10dp"
android:textSize="@dimen/font_size_28px" />
<TextView
android:id="@+id/initial_kb_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:text="/kbps"
android:textColor="#747A8C"
android:textSize="11sp" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:layout_height="@dimen/view_height_size_45">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/audio_bitrate"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<EditText
android:id="@+id/audio_bitrate"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="28dp"
android:background="@null"
android:hint="@string/audio_bitrate_value"
android:inputType="number"
android:textColor="#747A8C"
android:textColorHint="#747A8C"
android:maxLength="9"
android:paddingLeft="10dp"
android:textSize="@dimen/font_size_28px" />
<TextView
android:id="@+id/audio_kb_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:text="/kbps"
android:textColor="#E6E7EC"
android:textSize="11sp" />
</FrameLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="39dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:background="#141416"
android:gravity="center_vertical"
android:text="@string/min_fps"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_24px" />
<FrameLayout
android:layout_width="match_parent"
android:paddingEnd="20dp"
android:layout_height="@dimen/view_height_size_45">
<SeekBar
android:id="@+id/min_fps_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="30dp"
android:maxHeight="5dp"
android:minHeight="5dp"
android:progress="0"
android:theme="@style/Push_SeekBar_Style" />
<TextView
android:id="@+id/min_fps_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="20dp"
android:text="8"
android:textColor="#E6E7EC"
android:textSize="11sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:text="/fps"
android:textColor="#E6E7EC"
android:textSize="11sp" />
</FrameLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="39dp"
android:background="#141416"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:gravity="center_vertical"
android:text="@string/captrue_fps"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_24px" />
<FrameLayout
android:layout_width="match_parent"
android:paddingEnd="20dp"
android:layout_height="@dimen/view_height_size_45">
<SeekBar
android:id="@+id/fps_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="30dp"
android:maxHeight="5dp"
android:minHeight="5dp"
android:progress="80"
android:theme="@style/Push_SeekBar_Style" />
<TextView
android:id="@+id/fps_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_marginEnd="20dp"
android:text="@string/init_fps"
android:textColor="#E6E7EC"
android:textSize="11sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:text="/fps"
android:textColor="#E6E7EC"
android:textSize="11sp" />
</FrameLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="39dp"
android:background="#141416"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@string/audio_sampling_rate"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_24px" />
<FrameLayout
android:layout_width="match_parent"
android:paddingEnd="20dp"
android:layout_height="@dimen/view_height_size_45">
<SeekBar
android:id="@+id/audio_rate_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="30dp"
android:maxHeight="5dp"
android:minHeight="5dp"
android:progress="80"
android:theme="@style/Push_SeekBar_Style" />
<TextView
android:id="@+id/audio_rate_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:text="@string/setting_audio_480"
android:textColor="#E6E7EC"
android:textSize="11sp" />
</FrameLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="39dp"
android:background="#141416"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@string/GOP"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_24px" />
<FrameLayout
android:layout_width="match_parent"
android:paddingEnd="20dp"
android:layout_height="@dimen/view_height_size_45">
<SeekBar
android:id="@+id/gop_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="30dp"
android:maxHeight="5dp"
android:minHeight="5dp"
android:progress="40"
android:theme="@style/Push_SeekBar_Style" />
<TextView
android:id="@+id/gop_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:text="2/s"
android:textColor="#E6E7EC"
android:textSize="11sp" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/audio_profile"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<TextView
android:id="@+id/audio_profiles"
android:layout_width="100dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:gravity="center_vertical|end"
android:paddingEnd="23dp"
android:text="@string/setting_audio_aac_lc"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_live_arrow_right" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/sound_track"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<TextView
android:id="@+id/audio_channel"
android:layout_width="100dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:gravity="center_vertical|end"
android:paddingEnd="23dp"
android:text="@string/dual_track"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_live_arrow_right" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/video_encoder"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<TextView
android:id="@+id/video_encoder_type"
android:layout_width="100dp"
android:layout_height="match_parent"
android:gravity="center_vertical|end"
android:layout_gravity="end"
android:paddingEnd="23dp"
android:text="@string/h264"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_live_arrow_right" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/b_frames"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<TextView
android:id="@+id/b_frame_num"
android:layout_width="100dp"
android:layout_height="match_parent"
android:gravity="center_vertical|end"
android:layout_gravity="end"
android:paddingEnd="23dp"
android:text="0"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_live_arrow_right" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:layout_height="@dimen/view_height_size_45">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/hardware_encode"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/hard_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="true"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<LinearLayout
android:layout_width="match_parent"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:layout_height="@dimen/view_height_size_45"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:layout_weight="1"
android:text="@string/audio_hardware_encode"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/audio_hardenc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="304dp"
android:background="#1C1D22"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/cancel"
android:gravity="center"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="@string/pull_cancel"
android:textSize="15sp"
android:textColor="#4DCFE1"
android:layout_width="62dp"
android:layout_height="39dp"/>
<TextView
android:id="@+id/confirm_button"
android:gravity="center"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="@string/btn_confirm"
android:textSize="15sp"
android:textColor="#4DCFE1"
android:layout_width="62dp"
android:layout_height="39dp"/>
<View
android:id="@+id/div"
app:layout_constraintTop_toBottomOf="@+id/cancel"
android:layout_width="match_parent"
android:background="#3A3D48"
android:layout_height="1px"/>
<com.alivc.live.commonui.configview.OptionSelectorView
android:id="@+id/optionSelectorView"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/div"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,838 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#141416"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="39dp"
android:background="#141416" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:orientation="horizontal"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/push_orientation"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<TextView
android:id="@+id/main_orientation"
android:layout_width="100dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:gravity="center_vertical|end"
android:maxLines="1"
android:layout_marginEnd="23dp"
android:text="@string/portrait"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_live_arrow_right" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:orientation="horizontal"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/landscape_model"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<TextView
android:id="@+id/setting_display_mode"
android:layout_width="100dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:gravity="center_vertical|end"
android:paddingEnd="23dp"
android:text="@string/display_mode_cut"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_live_arrow_right" />
</FrameLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="39dp"
android:background="#141416"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@string/auto_reconnect"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_24px" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:paddingStart="20dp"
android:orientation="horizontal"
android:paddingEnd="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/reconnect_duration"
android:textColor="#FCFCFD"
android:layout_marginEnd="8dp"
android:textSize="@dimen/font_size_30px" />
<EditText
android:id="@+id/retry_interval"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="end|center_vertical"
android:background="@null"
android:hint="@string/input_desc"
android:inputType="number"
android:maxLength="9"
android:textColor="#747A8C"
android:textColorHint="#747A8C"
android:textSize="@dimen/font_size_28px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="/ms"
android:textColor="#747A8C"
android:textSize="11sp" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:paddingStart="20dp"
android:orientation="horizontal"
android:paddingEnd="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/reconnect_times"
android:textColor="#FCFCFD"
android:layout_marginEnd="8dp"
android:textSize="@dimen/font_size_30px" />
<EditText
android:id="@+id/retry_count"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="end|center_vertical"
android:background="@null"
android:hint="@string/input_desc"
android:inputType="number"
android:textColor="#747A8C"
android:textColorHint="#747A8C"
android:textSize="@dimen/font_size_28px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_gravity="center_vertical"
android:text="@string/second_rate"
android:textColor="#747A8C"
android:textSize="11sp" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="39dp"
android:background="#141416"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@string/watermark"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_24px" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/watermark_open"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/watermark_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/watermark_setting"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<TextView
android:id="@+id/water_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:text="@string/watermark_setting"
android:textColor="#747A8C"
android:textSize="14sp" />
</FrameLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="39dp"
android:background="#141416"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@string/mirror_desc"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_24px" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/setting_push_mirror"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/push_mirror_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/setting_pre_mirror"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/preview_mirror_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="39dp"
android:background="#141416"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@string/camera_setting"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_24px" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/front_camera"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/camera_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="true"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/auto_focus"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/autofocus_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/camera_capture_output_preference"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<TextView
android:id="@+id/setting_camera_capture_output_preference"
android:layout_width="100dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:gravity="center_vertical|end"
android:paddingEnd="23dp"
android:text="@string/camera_capture_output_preference_preview"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_live_arrow_right" />
</FrameLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="39dp"
android:background="#141416"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@string/beauty_setting"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_24px" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/beauty_enable"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/beautyOn_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<TextView
android:layout_width="match_parent"
android:layout_height="39dp"
android:background="#141416"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@string/image_push"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_24px" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/pause_image"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/pause_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="true"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/network_image"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/network_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="true"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="39dp"
android:background="#141416" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:orientation="horizontal"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/push_mode"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<TextView
android:id="@+id/push_mode"
android:layout_width="100dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:gravity="center_vertical|end"
android:paddingEnd="23dp"
android:text="@string/video_push_streaming"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:src="@drawable/ic_live_arrow_right" />
</FrameLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="39dp"
android:background="#141416"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@string/url_auth"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_24px" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:orientation="horizontal"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/auth_text"
android:layout_marginEnd="8dp"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<EditText
android:id="@+id/auth_time"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="end|center_vertical"
android:background="@null"
android:hint="@string/input_desc"
android:inputType="number"
android:textColor="#747A8C"
android:textColorHint="#747A8C"
android:textSize="@dimen/font_size_28px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="/ms"
android:textColor="#747A8C"
android:textSize="11sp" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/privacy_text"
android:textColor="#FCFCFD"
android:layout_marginEnd="8dp"
android:textSize="@dimen/font_size_30px" />
<EditText
android:id="@+id/privacy_key"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:gravity="end|center_vertical"
android:background="@null"
android:hint="@string/input_desc"
android:inputType="number"
android:textColor="#747A8C"
android:textColorHint="#747A8C"
android:textSize="@dimen/font_size_28px" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="39dp"
android:background="#141416"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:text="@string/other_func"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_24px" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/asynchronous_interface"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/async_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/music_mode"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/music_mode_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/extern_stream_main"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/extern_video"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_marginStart="20dp"
android:background="#3A3D48" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:gravity="center_vertical"
android:paddingStart="20dp"
android:paddingEnd="20dp">
<TextView
android:id="@+id/local_log"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="100dp"
android:text="@string/local_log"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_30px" />
<Switch
android:id="@+id/log_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_gravity="end|center_vertical"
android:checked="false"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track" />
</FrameLayout>
</LinearLayout>

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_size_45"
android:paddingEnd="20dp"
android:orientation="horizontal">
<SeekBar
android:id="@+id/resolution_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="30dp"
android:maxHeight="5dp"
android:minHeight="5dp"
android:progress="66"
android:theme="@style/Push_SeekBar_Style" />
<TextView
android:id="@+id/resolution_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:text="@string/setting_resolution_540P"
android:textColor="#FCFCFD"
android:textSize="12sp" />
</FrameLayout>
<LinearLayout
android:id="@+id/custom_resolution_root"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="20dp"
android:visibility="gone"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:textColor="#FCFCFD"
android:text="@string/resolution_width"/>
<EditText
android:id="@+id/et_width"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:maxLength="10"
android:maxLines="1"
android:text="0"
android:textColor="#FCFCFD"
android:inputType="number"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:textColor="#FCFCFD"
android:text="@string/resolution_height"/>
<EditText
android:id="@+id/et_height"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:maxLength="10"
android:maxLines="1"
android:text="0"
android:textColor="#FCFCFD"
android:inputType="number"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="10dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="70dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/tv_sei_payload" />
<EditText
android:id="@+id/et_sei_payload_type"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:gravity="center"
android:inputType="number"
android:maxLines="1"
android:textColor="@color/color_text_white"
android:textSize="@dimen/font_size_32px" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="70dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/tv_sei_message" />
<EditText
android:id="@+id/et_sei"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:maxLines="1"
android:textColor="@color/color_text_white"
android:textSize="@dimen/font_size_32px" />
</LinearLayout>
<Button
android:id="@+id/btn_send_sei"
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="@drawable/shape_pysh_btn_bg"
android:gravity="center"
android:includeFontPadding="false"
android:text="@string/sei_send_custom_message_tv"
android:textColor="#FCFCFD"
android:textSize="@dimen/font_size_24px"
android:textStyle="normal" />
</LinearLayout>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="@+id/push_args_setting"
layout="@layout/push_args_setting_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include
android:id="@+id/push_function_setting"
layout="@layout/push_function_setting_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</LinearLayout>

View File

@@ -0,0 +1,412 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/darker_gray"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingBottom="20dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal"
android:gravity="center">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/watermark_setting"
android:textColor="#747A8C"
android:textSize="@dimen/font_size_28px" />
</LinearLayout>
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal">
<Switch
android:id="@+id/watermark"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track"
android:checked="true"
android:visibility="gone"/>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_weight="1">
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical"
android:padding="5dp">
<TextView
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="X"
android:textColor="@android:color/black"
android:textSize="@dimen/font_size_28px" />
<EditText
android:id="@+id/x"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/shape_rect_gray"
android:hint="0.1"
android:paddingLeft="10dp"
android:inputType="numberDecimal"
android:textColor="@android:color/black"
android:textColorHint="@android:color/darker_gray"
android:textSize="@dimen/font_size_28px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="%"/>
</LinearLayout>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical"
android:padding="5dp">
<TextView
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="Y"
android:textColor="@android:color/black"
android:textSize="@dimen/font_size_28px" />
<EditText
android:id="@+id/y"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/shape_rect_gray"
android:hint="0.2"
android:paddingLeft="10dp"
android:inputType="numberDecimal"
android:textColor="@android:color/black"
android:textColorHint="@android:color/darker_gray"
android:textSize="@dimen/font_size_28px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="%"/>
</LinearLayout>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical"
android:padding="5dp">
<TextView
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="W"
android:textColor="@android:color/black"
android:textSize="@dimen/font_size_28px" />
<EditText
android:id="@+id/w"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/shape_rect_gray"
android:hint="0.1"
android:paddingLeft="10dp"
android:inputType="numberDecimal"
android:textColor="@android:color/black"
android:textColorHint="@android:color/darker_gray"
android:textSize="@dimen/font_size_28px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="%"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<View
android:layout_width="wrap_content"
android:layout_height="1dp"
android:background="@android:color/black" />
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal">
<Switch
android:id="@+id/watermark1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track"
android:checked="true"
android:visibility="gone"/>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_weight="1">
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical"
android:padding="5dp">
<TextView
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="X"
android:textColor="@android:color/black"
android:textSize="@dimen/font_size_28px" />
<EditText
android:id="@+id/x1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/shape_rect_gray"
android:hint="0.1"
android:paddingLeft="10dp"
android:inputType="numberDecimal"
android:textColor="@android:color/black"
android:textColorHint="@android:color/darker_gray"
android:textSize="@dimen/font_size_28px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="%"/>
</LinearLayout>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical"
android:padding="5dp">
<TextView
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="Y"
android:textColor="@android:color/black"
android:textSize="@dimen/font_size_28px" />
<EditText
android:id="@+id/y1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/shape_rect_gray"
android:hint="0.3"
android:paddingLeft="10dp"
android:inputType="numberDecimal"
android:textColor="@android:color/black"
android:textColorHint="@android:color/darker_gray"
android:textSize="@dimen/font_size_28px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="%"/>
</LinearLayout>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical"
android:padding="5dp">
<TextView
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="W"
android:textColor="@android:color/black"
android:textSize="@dimen/font_size_28px" />
<EditText
android:id="@+id/w1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/shape_rect_gray"
android:hint="0.1"
android:paddingLeft="10dp"
android:inputType="numberDecimal"
android:textColor="@android:color/black"
android:textColorHint="@android:color/darker_gray"
android:textSize="@dimen/font_size_28px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="%"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<View
android:layout_width="wrap_content"
android:layout_height="1dp"
android:background="@android:color/black" />
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal">
<Switch
android:id="@+id/watermark2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:track="@drawable/track"
android:checked="true"
android:visibility="gone"/>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_weight="1">
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical"
android:padding="5dp">
<TextView
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="X"
android:textColor="@android:color/black"
android:textSize="@dimen/font_size_28px" />
<EditText
android:id="@+id/x2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/shape_rect_gray"
android:hint="0.1"
android:paddingLeft="10dp"
android:inputType="numberDecimal"
android:textColor="@android:color/black"
android:textColorHint="@android:color/darker_gray"
android:textSize="@dimen/font_size_28px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="%"/>
</LinearLayout>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical"
android:padding="5dp">
<TextView
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="Y"
android:textColor="@android:color/black"
android:textSize="@dimen/font_size_28px" />
<EditText
android:id="@+id/y2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/shape_rect_gray"
android:hint="0.5"
android:paddingLeft="10dp"
android:inputType="numberDecimal"
android:textColor="@android:color/black"
android:textColorHint="@android:color/darker_gray"
android:textSize="@dimen/font_size_28px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="%"/>
</LinearLayout>
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical"
android:padding="5dp">
<TextView
android:layout_width="40dp"
android:layout_height="wrap_content"
android:text="W"
android:textColor="@android:color/black"
android:textSize="@dimen/font_size_28px" />
<EditText
android:id="@+id/w2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/shape_rect_gray"
android:hint="0.1"
android:paddingLeft="10dp"
android:inputType="numberDecimal"
android:textColor="@android:color/black"
android:textColorHint="@android:color/darker_gray"
android:textSize="@dimen/font_size_28px" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:text="%"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="system_error">System error:</string>
<string name="sdk_error">SDK error</string>
<string name="connect_fail">Connection failed</string>
<string name="view_string_hint_push_url">Enter the ingest URL.</string>
<string name="url_empty">Please enter url</string>
<string name="btn_confirm">Confirm</string>
<string name="pull_cancel">Cancel</string>
<string name="stop_button">Stop pushing</string>
<string name="start_push">Start pushing</string>
<string name="resolution_label">Resolution</string>
<string name="setting_resolution_180P">180P</string>
<string name="setting_resolution_240P">240P</string>
<string name="setting_resolution_360P">360P</string>
<string name="setting_resolution_480P">480P</string>
<string name="setting_resolution_540P">540P</string>
<string name="setting_resolution_720P">720P</string>
<string name="setting_resolution_1080P">1080P</string>
<string name="setting_resolution_self_define">Self Define</string>
<string name="bitrate_control">Bitrate Control</string>
<string name="variable_resolution">Variable Resolution</string>
<string name="resolution_width">width</string>
<string name="resolution_height">height</string>
<string name="resolution_label_desc">To enable the system to automatically switch to the most suitable resolution, you must enable adaptive bitrate streaming. For more information, see the API reference.</string>
<string name="advance_config">Advanced Settings</string>
<string name="display_mode">DisplayMode</string>
<string name="display_mode_full">scaleToFill</string>
<string name="display_mode_fit">aspectFit</string>
<string name="display_mode_cut">aspectFill</string>
<string name="quality_resolution_first">High quality priority</string>
<string name="quality_fluency_first">fluency priority</string>
<string name="video_label_desc">You can adjust the video bitrate and frame rate only in custom mode.</string>
<string name="quality_custom">custom</string>
<string name="target_bitrate">Target bitrate</string>
<string name="target_rate_value">1200</string>
<string name="min_bitrate">Min bitrate</string>
<string name="min_rate_value">300</string>
<string name="initial_bitrate">Initial bitrate</string>
<string name="audio_bitrate">Audio bitrate</string>
<string name="initial_bit_value">1000</string>
<string name="audio_bitrate_value">64</string>
<string name="min_fps">Min fps</string>
<string name="captrue_fps">Captrue fps</string>
<string name="init_fps">20</string>
<string name="audio_sampling_rate">Audio sampling rate</string>
<string name="setting_audio_480">48KHz</string>
<string name="setting_audio_441">44.1KHz</string>
<string name="setting_audio_320">32KHz</string>
<string name="setting_audio_160">16KHz</string>
<string name="GOP">Gop</string>
<string name="audio_profile">Audio profile</string>
<string name="sound_track">Sound track</string>
<string name="video_encoder">Video Encoder</string>
<string name="b_frames">B Frames</string>
<string name="hardware_encode">Hardware encoder</string>
<string name="audio_hardware_encode">Audio Hardware encoder</string>
<string name="setting_audio_aac_lc">LC</string>
<string name="setting_audio_aac_he">HE-AAC</string>
<string name="setting_audio_aac_hev2">HEv2-AAC</string>
<string name="setting_audio_aac_ld">AAC-LD</string>
<string name="single_track">Single track</string>
<string name="dual_track">Dual track</string>
<string name="h264">H264</string>
<string name="h265">H265</string>
<string name="portrait">Portrait</string>
<string name="homeLeft">HomeLeft</string>
<string name="homeRight">HomeRight</string>
<string name="video_push_streaming">Default</string>
<string name="audio_only_push_streaming">Audio only</string>
<string name="video_only_push_streaming">Video only</string>
<string name="push_orientation">Ingest To</string>
<string name="landscape_model">Orientation</string>
<string name="auto_reconnect">Auto Reconnection</string>
<string name="reconnect_duration">Reconnection duration</string>
<string name="input_desc">Enter</string>
<string name="reconnect_times">Reconnection times</string>
<string name="watermark">Watermark</string>
<string name="watermark_open">Watermark</string>
<string name="watermark_setting">Setting</string>
<string name="mirror_desc">Mirroring</string>
<string name="setting_pre_mirror">Preview mirror</string>
<string name="setting_push_mirror">Push mirror</string>
<string name="camera_setting">Camera Control</string>
<string name="front_camera">Front camera</string>
<string name="auto_focus">Auto focus</string>
<string name="camera_capture_output_preference">Camera Capture Output Preference</string>
<string name="camera_capture_output_preference_auto">Auto</string>
<string name="camera_capture_output_preference_performance">Performance</string>
<string name="camera_capture_output_preference_preview">Preview</string>
<string name="beauty_setting">Retouching Switch</string>
<string name="beauty_enable">Beauty</string>
<string name="image_push">Ingest Standby Stream</string>
<string name="pause_image">Pause Image</string>
<string name="network_image">NetworkPoor Image</string>
<string name="push_mode">Push Mode</string>
<string name="url_auth">Url Auth Setting</string>
<string name="auth_text">Auth Time</string>
<string name="privacy_text">Privacy Key</string>
<string name="other_func">Other Features</string>
<string name="asynchronous_interface">Asynchronous interface</string>
<string name="music_mode">Music Mode</string>
<string name="extern_stream_main">Main Extern Stream</string>
<string name="local_log">Local log</string>
<string name="push_args">Stream Ingest Parameters</string>
<string name="stream_pusher_tip">Stream Ingest Features</string>
<string name="tv_sei_message">message</string>
<string name="tv_sei_payload">payload</string>
<string name="local_record_tv">Local Record</string>
<string name="manual_create_egl_context_tv">Manual Create EGLContext</string>
<string name="sei_send_custom_message_tv">Send Custom Message</string>
</resources>

View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="system_error">系统错误:</string>
<string name="sdk_error">SDK错误</string>
<string name="connect_fail">连接失败</string>
<string name="view_string_hint_push_url">请输入推流url</string>
<string name="url_empty">请输入推流地址</string>
<string name="btn_confirm">确认</string>
<string name="pull_cancel">取消</string>
<string name="stop_button">停止推流</string>
<string name="start_push">开始推流</string>
<string name="resolution_label">分辨率</string>
<string name="setting_resolution_180P">180P</string>
<string name="setting_resolution_240P">240P</string>
<string name="setting_resolution_360P">360P</string>
<string name="setting_resolution_480P">480P</string>
<string name="setting_resolution_540P">540P</string>
<string name="setting_resolution_720P">720P</string>
<string name="setting_resolution_1080P">1080P</string>
<string name="setting_resolution_self_define">自定义</string>
<string name="bitrate_control">码率自适应</string>
<string name="resolution_width">宽:</string>
<string name="resolution_height">高:</string>
<string name="variable_resolution">可变分辨率</string>
<string name="resolution_label_desc">分辨率自适应需开启码率自适应详细请参考api文档</string>
<string name="advance_config">高级设置</string>
<string name="display_mode">显示模式</string>
<string name="display_mode_full">拉伸</string>
<string name="display_mode_fit">适合</string>
<string name="display_mode_cut">剪裁</string>
<string name="quality_resolution_first">清晰度优先</string>
<string name="quality_fluency_first">流畅度优先</string>
<string name="video_label_desc">视频码率和帧率仅在自定义模式下可以调整</string>
<string name="quality_custom">自定义</string>
<string name="target_bitrate">视频目标码率</string>
<string name="target_rate_value">1200</string>
<string name="min_bitrate">视频最小码率</string>
<string name="min_rate_value">300</string>
<string name="initial_bitrate">视频初始码率</string>
<string name="initial_bit_value">1000</string>
<string name="audio_bitrate">音频码率</string>
<string name="audio_bitrate_value">64</string>
<string name="min_fps">最小帧率</string>
<string name="captrue_fps">采集帧率</string>
<string name="init_fps">20</string>
<string name="audio_sampling_rate">音频采样率</string>
<string name="setting_audio_480">48KHz</string>
<string name="setting_audio_441">44.1KHz</string>
<string name="setting_audio_320">32KHz</string>
<string name="setting_audio_160">16KHz</string>
<string name="GOP">关键帧间隔</string>
<string name="audio_profile">音频编码</string>
<string name="sound_track">声道</string>
<string name="video_encoder">视频编码器</string>
<string name="b_frames">B帧个数</string>
<string name="hardware_encode">视频硬编码</string>
<string name="audio_hardware_encode">音频硬编码</string>
<string name="setting_audio_aac_lc">LC</string>
<string name="setting_audio_aac_he">HE-AAC</string>
<string name="setting_audio_aac_hev2">HEv2-AAC</string>
<string name="setting_audio_aac_ld">AAC-LD</string>
<string name="single_track">单声道</string>
<string name="dual_track">双声道</string>
<string name="h264">H264</string>
<string name="h265">H265</string>
<string name="portrait">Portrait</string>
<string name="homeLeft">HomeLeft</string>
<string name="homeRight">HomeRight</string>
<string name="video_push_streaming">音视频</string>
<string name="audio_only_push_streaming">纯音频</string>
<string name="video_only_push_streaming">纯视频</string>
<string name="push_orientation">推流方向</string>
<string name="landscape_model">显示模式</string>
<string name="auto_reconnect">自动重连</string>
<string name="reconnect_duration">重连时长</string>
<string name="input_desc">请输入</string>
<string name="reconnect_times">重连次数</string>
<string name="watermark">水印</string>
<string name="watermark_open">开启水印</string>
<string name="watermark_setting">水印位置</string>
<string name="mirror_desc">镜像</string>
<string name="setting_pre_mirror">预览镜像</string>
<string name="setting_push_mirror">推流镜像</string>
<string name="camera_setting">相机控制</string>
<string name="front_camera">前置摄像头</string>
<string name="auto_focus">自动对焦</string>
<string name="camera_capture_output_preference">摄像头采集偏好</string>
<string name="camera_capture_output_preference_auto">自动</string>
<string name="camera_capture_output_preference_performance">性能</string>
<string name="camera_capture_output_preference_preview">高清预览</string>
<string name="beauty_setting">美颜开关</string>
<string name="beauty_enable">开启美颜</string>
<string name="image_push">图片推流</string>
<string name="pause_image">暂停图片</string>
<string name="network_image">网络差图片</string>
<string name="push_mode">推流模式</string>
<string name="url_auth">推流鉴权</string>
<string name="auth_text">有效时长</string>
<string name="privacy_text">鉴权key</string>
<string name="other_func">其他功能</string>
<string name="asynchronous_interface">异步接口</string>
<string name="music_mode">音乐模式</string>
<string name="extern_stream_main">外部音视频</string>
<string name="local_log">本地日志</string>
<string name="push_args">推流参数</string>
<string name="stream_pusher_tip">推流功能</string>
<string name="second_rate">/次</string>
<string name="tv_sei_message">message</string>
<string name="tv_sei_payload">payload</string>
<string name="local_record_tv">本地录制</string>
<string name="manual_create_egl_context_tv">手动创建EGLContext</string>
<string name="sei_send_custom_message_tv">发送自定义消息</string>
<string name="backdoor_title">Live Push Demo配置页面</string>
<string name="backdoor_tip">用于手动配置demo相应功能</string>
<string name="backdoor_multi_pk_16in">16方多人PK</string>
</resources>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--MaxHeightLayout 使用-->
<declare-styleable name="mMaxRatio">
<attr name="linear_max_ratio" format="float"></attr>
</declare-styleable>
<declare-styleable name="av_actionbar">
<attr name="showTitle" format="boolean" />
<attr name="showLeftView" format="boolean" />
<attr name="showRightView" format="boolean" />
<attr name="title" format="string" />
<attr name="leftImageBackground" format="reference" />
<attr name="leftImageSrc" format="reference" />
<attr name="rightImageBackground" format="reference" />
<attr name="rightImageSrc" format="reference" />
</declare-styleable>
</resources>

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="system_error">System error:</string>
<string name="sdk_error">SDK error</string>
<string name="connect_fail">Connection failed</string>
<string name="view_string_hint_push_url">Enter the ingest URL.</string>
<string name="url_empty">Please enter url</string>
<string name="btn_confirm">Confirm</string>
<string name="pull_cancel">Cancel</string>
<string name="stop_button">Stop pushing</string>
<string name="start_push">Start pushing</string>
<string name="resolution_label">Resolution</string>
<string name="setting_resolution_180P">180P</string>
<string name="setting_resolution_240P">240P</string>
<string name="setting_resolution_360P">360P</string>
<string name="setting_resolution_480P">480P</string>
<string name="setting_resolution_540P">540P</string>
<string name="setting_resolution_720P">720P</string>
<string name="setting_resolution_1080P">1080P</string>
<string name="setting_resolution_self_define">Self Define</string>
<string name="bitrate_control">Bitrate Control</string>
<string name="variable_resolution">Variable Resolution</string>
<string name="resolution_width">width</string>
<string name="resolution_height">height</string>
<string name="resolution_label_desc">To enable the system to automatically switch to the most suitable resolution, you must enable adaptive bitrate streaming. For more information, see the API reference.</string>
<string name="advance_config">Advanced Settings</string>
<string name="display_mode">DisplayMode</string>
<string name="display_mode_full">scaleToFill</string>
<string name="display_mode_fit">aspectFit</string>
<string name="display_mode_cut">aspectFill</string>
<string name="quality_resolution_first">High quality priority</string>
<string name="quality_fluency_first">fluency priority</string>
<string name="video_label_desc">You can adjust the video bitrate and frame rate only in custom mode.</string>
<string name="quality_custom">custom</string>
<string name="target_bitrate">Target bitrate</string>
<string name="target_rate_value">1200</string>
<string name="min_bitrate">Min bitrate</string>
<string name="min_rate_value">300</string>
<string name="initial_bitrate">Initial bitrate</string>
<string name="audio_bitrate">Audio bitrate</string>
<string name="initial_bit_value">1000</string>
<string name="audio_bitrate_value">64</string>
<string name="min_fps">Min fps</string>
<string name="captrue_fps">Captrue fps</string>
<string name="init_fps">20</string>
<string name="audio_sampling_rate">Audio sampling rate</string>
<string name="setting_audio_480">48KHz</string>
<string name="setting_audio_441">44.1KHz</string>
<string name="setting_audio_320">32KHz</string>
<string name="setting_audio_160">16KHz</string>
<string name="GOP">Gop</string>
<string name="audio_profile">Audio profile</string>
<string name="sound_track">Sound track</string>
<string name="video_encoder">Video Encoder</string>
<string name="b_frames">B Frames</string>
<string name="hardware_encode">Hardware encoder</string>
<string name="audio_hardware_encode">Audio Hardware encoder</string>
<string name="setting_audio_aac_lc">LC</string>
<string name="setting_audio_aac_he">HE-AAC</string>
<string name="setting_audio_aac_hev2">HEv2-AAC</string>
<string name="setting_audio_aac_ld">AAC-LD</string>
<string name="single_track">Single track</string>
<string name="dual_track">Dual track</string>
<string name="h264">H264</string>
<string name="h265">H265</string>
<string name="portrait">Portrait</string>
<string name="homeLeft">HomeLeft</string>
<string name="homeRight">HomeRight</string>
<string name="video_push_streaming">Default</string>
<string name="audio_only_push_streaming">Audio only</string>
<string name="video_only_push_streaming">Video only</string>
<string name="push_orientation">Ingest To</string>
<string name="landscape_model">Orientation</string>
<string name="auto_reconnect">Auto Reconnection</string>
<string name="reconnect_duration">Reconnection duration</string>
<string name="input_desc">Enter</string>
<string name="reconnect_times">Reconnection times</string>
<string name="watermark">Watermark</string>
<string name="watermark_open">Watermark</string>
<string name="watermark_setting">Setting</string>
<string name="mirror_desc">Mirroring</string>
<string name="setting_pre_mirror">Preview mirror</string>
<string name="setting_push_mirror">Push mirror</string>
<string name="camera_setting">Camera Control</string>
<string name="front_camera">Front camera</string>
<string name="auto_focus">Auto focus</string>
<string name="camera_capture_output_preference">Camera Capture Output Preference</string>
<string name="camera_capture_output_preference_auto">Auto</string>
<string name="camera_capture_output_preference_performance">Performance</string>
<string name="camera_capture_output_preference_preview">Preview</string>
<string name="beauty_setting">Retouching Switch</string>
<string name="beauty_enable">Beauty</string>
<string name="image_push">Ingest Standby Stream</string>
<string name="pause_image">Pause Image</string>
<string name="network_image">NetworkPoor Image</string>
<string name="push_mode">Push Mode</string>
<string name="url_auth">Url Auth Setting</string>
<string name="auth_text">Auth Time</string>
<string name="privacy_text">Privacy Key</string>
<string name="other_func">Other Features</string>
<string name="asynchronous_interface">Asynchronous interface</string>
<string name="music_mode">Music Mode</string>
<string name="extern_stream_main">Main Extern Stream</string>
<string name="local_log">Local log</string>
<string name="push_args">Stream Ingest Parameters</string>
<string name="stream_pusher_tip">Stream Ingest Features</string>
<string name="second_rate">/Times</string>
<string name="tv_sei_message">message</string>
<string name="tv_sei_payload">payload</string>
<string name="local_record_tv">Local Record</string>
<string name="manual_create_egl_context_tv">Manual Create EGLContext</string>
<string name="sei_send_custom_message_tv">Send Custom Message</string>
</resources>

View File

@@ -0,0 +1,66 @@
<resources>
<style name="AVLiveBaseTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:windowTranslucentStatus">true</item>
</style>
<style name="AVLiveAppTheme" parent="AVLiveBaseTheme">
</style>
<style name="AVLive_ProgressBar_Horizontal_Style" parent="Widget.AppCompat.ProgressBar.Horizontal">
<item name="colorAccent">#4DCFE1</item>
</style>
<!-- Base application theme. -->
<style name="AVLiveTheme" parent="AVLiveBaseTheme">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="dialog_animation">
<item name="android:windowEnterAnimation">@anim/anim_trans_from_bottom</item>
<item name="android:windowExitAnimation">@anim/anim_layout_bottom_out</item>
</style>
<style name="DialogStyle">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:cacheColorHint">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
<style name="AUILiveDialog" parent="android:Theme.Dialog">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowAnimationStyle">@null</item>
<!-- 为了避免在有 NavigationBar 的手机上 Dialog 从 NavigationBar 底部上来。去掉 Dialog 的动画,使用 View 的动画。-->
<item name="android:layout_width">wrap_content</item>
<item name="android:padding">0dp</item>
</style>
<style name="LogText">
<item name="android:textColor">@color/colorAccent</item>
<item name="android:textSize">@dimen/font_size_28px</item>
<item name="android:gravity">center_vertical</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:padding">0dp</item>
</style>
<style name="Push_SeekBar_Style" parent="Widget.AppCompat.SeekBar">
<item name="colorAccent">#4DCFE1</item>
</style>
<style name="AUIEditText">
<item name="colorControlNormal">#3A3D48</item>
<item name="colorControlActivated">#3A3D48</item>
</style>
<style name="Live.BaseDialog" parent="Theme.AppCompat.Dialog">
</style>
<style name="Live.BottomSheetDialog" parent="Live.BaseDialog">
<item name="android:backgroundDimAmount">0</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
</resources>

View File

@@ -0,0 +1,10 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="LiveBaseActionBar_Style">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">44dp</item>
</style>
<color name="text_true_color">#FF6500</color>
<color name="des_txt_color">#8016243C</color>
</resources>

View File

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

View File

@@ -0,0 +1,33 @@
plugins {
id 'com.android.library'
}
android {
compileSdkVersion androidCompileSdkVersion
buildToolsVersion androidBuildToolsVersion
defaultConfig {
minSdkVersion androidMinSdkVersion
targetSdkVersion androidTargetSdkVersion
versionCode 1
versionName "1.0"
consumerProguardFiles "consumer-rules.pro"
}
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 {
api externalOKHTTP
implementation externalAndroidAnnotation
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.alivc.live.commonutils">
</manifest>

View File

@@ -0,0 +1,353 @@
package com.alivc.live.commonutils;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ConfigurationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.provider.Settings;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.UUID;
/**
* @author keria
* @date 2021/7/27
*/
public class AppUtil {
private static final String BUILD_TYPE_DEBUG = "DEBUG";
private static final String BUILD_TYPE_RELEASE = "RELEASE";
private static final String CPU_INFO_FILE_PATH = "/proc/cpuinfo";
// read from cpu info file only once to reduce method cost.
private static final ArrayList<String> mCpuInfo = new ArrayList<>();
private AppUtil() {
}
/**
* Get app name
*
* @param context android context
* @return app name, not null
*/
@NonNull
public static String getAppName(@Nullable Context context) {
if (context == null) {
return "";
}
try {
ApplicationInfo ai = context.getApplicationInfo();
PackageManager pm = context.getPackageManager();
return ai.loadLabel(pm).toString();
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
/**
* Get app id
*
* @param context android context
* @return app id, not null
*/
@NonNull
public static String getAppId(@Nullable Context context) {
if (context == null) {
return "";
}
String pn = context.getPackageName();
return (pn != null) ? pn : "";
}
/**
* Get app build type
*
* @param context android context
* @return app build type, debug or release
*/
@NonNull
public static String getApkBuildType(@Nullable Context context) {
if (context == null) {
return "";
}
boolean isDebug = false;
try {
ApplicationInfo ai = context.getApplicationInfo();
isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
} catch (Exception e) {
e.printStackTrace();
}
return isDebug ? BUILD_TYPE_DEBUG : BUILD_TYPE_RELEASE;
}
/**
* Get package version name
*
* @param context android context
* @return package version name
*/
@NonNull
public static String getPackageVersionName(@Nullable Context context) {
if (context == null) {
return "";
}
PackageManager manager = context.getPackageManager();
String name = null;
try {
PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
name = info.versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return name != null ? name : "";
}
/**
* Get package version code
*
* @param context android context
* @return package version code
*/
public static int getPackageVersionCode(@Nullable Context context) {
if (context == null) {
return 0;
}
PackageManager manager = context.getPackageManager();
int code = 0;
try {
PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
code = info.versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return code;
}
/**
* Get android device unique id
*
* @param context android context
* @return android device unique id, not null
* @link {https://developer.android.com/training/articles/user-data-ids}
*/
@SuppressLint("HardwareIds")
@NonNull
public static String getDeviceId(@Nullable Context context) {
if (context != null) {
try {
return Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
} catch (Exception e) {
e.printStackTrace();
}
}
return "uuid-" + UUID.randomUUID();
}
/**
* Get full cpu info
*
* @return full cpu info
*/
@NonNull
public static String getFullCPUInfo() {
if (mCpuInfo.isEmpty()) {
mCpuInfo.addAll(readFromCpuInfo());
}
StringBuilder sb = new StringBuilder();
for (String line : mCpuInfo) {
sb.append(line).append("\n");
}
return sb.toString().trim();
}
/**
* Get brief cpu info
*
* @return brief cpu info
*/
@NonNull
public static String getBriefCPUInfo() {
return getCPUHardwareInfo() + "\n" + getCPUProcessorArchInfo();
}
/**
* Get cpu processor name
*
* @return cpu processor name
*/
@NonNull
public static String getCPUProcessorArchInfo() {
return getSpecificCpuInfoByKey("Processor");
}
/**
* Get CPU hardware name
*
* @return cpu hardware name, not null
*/
@NonNull
public static String getCPUHardwareInfo() {
return getSpecificCpuInfoByKey("Hardware");
}
/**
* Get specific cpu info by key.
*
* @param key cpu info key
* @return cpu info value
*/
@NonNull
public static String getSpecificCpuInfoByKey(@NonNull String key) {
if (mCpuInfo.isEmpty()) {
mCpuInfo.addAll(readFromCpuInfo());
}
String value = "";
for (String str : mCpuInfo) {
if (str.startsWith(key)) {
String[] data = str.split(":");
if (data.length > 1) {
value = data[1].trim();
}
break;
}
}
return value;
}
/**
* Read from cpuinfo file
*
* @return file lines
*/
@NonNull
public static ArrayList<String> readFromCpuInfo() {
ArrayList<String> cpuInfo = new ArrayList<>();
try {
FileReader fr = new FileReader(CPU_INFO_FILE_PATH);
BufferedReader br = new BufferedReader(fr, 8192);
String str;
while ((str = br.readLine()) != null) {
cpuInfo.add(str.trim());
}
} catch (IOException e) {
e.printStackTrace();
}
return cpuInfo;
}
/**
* Get brief device info
*
* @return brief device info, not null
*/
@NonNull
public static String getBriefDeviceInfo() {
return DeviceUtil.getDeviceBrand()
+ " " + DeviceUtil.getDeviceModel()
+ ", Android " + DeviceUtil.getDeviceVersion()
+ ", " + DeviceUtil.getDeviceCPUArch();
}
/**
* Get detailed device info
*
* @return detailed device info, not null
*/
@SuppressLint("HardwareIds")
@NonNull
public static String getDeviceInfo() {
String s = "主板: " + Build.BOARD +
"\n系统启动程序版本号: " + Build.BOOTLOADER +
"\n系统定制商: " + Build.BRAND +
"\ncpu指令集: " + Build.CPU_ABI +
"\ncpu指令集2: " + Build.CPU_ABI2 +
"\n设置参数: " + Build.DEVICE +
"\n显示屏参数: " + Build.DISPLAY +
"\n无线电固件版本: " + Build.getRadioVersion() +
"\n硬件识别码: " + Build.FINGERPRINT +
"\n硬件名称: " + Build.HARDWARE +
"\nHOST: " + Build.HOST +
"\n修订版本列表: " + Build.ID +
"\n硬件制造商: " + Build.MANUFACTURER +
"\n版本: " + Build.MODEL +
"\n硬件序列号: " + Build.SERIAL +
"\n手机制造商: " + Build.PRODUCT +
"\n描述Build的标签: " + Build.TAGS +
"\nTIME: " + Build.TIME +
"\nbuilder类型: " + Build.TYPE +
"\nUSER: " + Build.USER;
return s.trim();
}
/**
* Get OpenGL ES info
*
* @param context android context, nullable
* @return OpenGL ES info, not null
*/
@NonNull
public static String getOpenGLESInfo(@Nullable Context context) {
if (context == null) {
return "";
}
try {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (am == null) {
return "";
}
ConfigurationInfo ci = am.getDeviceConfigurationInfo();
if (ci == null) {
return "";
}
return "OpenGL ES " + ci.getGlEsVersion() + " " + ci.reqGlEsVersion;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
/**
* Get android device network status
*
* @param context android context
* @return network connected, true or false
*/
public static boolean isNetworkAvailable(@Nullable Context context) {
if (context == null) {
return false;
}
try {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm == null) {
return false;
}
NetworkInfo ni = cm.getActiveNetworkInfo();
if (ni == null) {
return false;
}
return ni.isConnected();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}

View File

@@ -0,0 +1,153 @@
package com.alivc.live.commonutils;
import android.content.Context;
import android.content.res.AssetManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Created by baorunchen on 2021/4/29.
* <p>
* Android asset file util
*/
public class AssetUtil {
/**
* Copy asset file or folder to sdcard
*
* @param context android context
* @param srcFilePath src asset path, file or folder
* @param destFilePath dest sdcard path
* @return true->success, false->failed.
* <p>
* Such as:
* srcFilePath: ai.bundle/face.model
* destFilePath: /data/xxx/yyy/zzz/ai.bundle/face.model
* result: /data/xxx/yyy/zzz/ai.bundle/face.model
*/
public static boolean copyAssetToSdCard(@Nullable Context context, @Nullable String srcFilePath, @Nullable String destFilePath) {
if (context == null || srcFilePath == null || destFilePath == null || srcFilePath.isEmpty() || destFilePath.isEmpty()) {
return false;
}
ArrayList<String> assetFileList = getAssetFilePathList(context, srcFilePath);
boolean isFolder = !assetFileList.isEmpty();
// Check whether target file exists
File targetFile = new File(destFilePath);
// If file already exists, return.
// Only when target is a file and exists can return.
if (targetFile.exists() && targetFile.isFile()) {
return true;
}
if (!isFolder) {
return copyAssetFileToSdCard(context, srcFilePath, destFilePath);
} else {
// If file exists but not expected file type, remove it.
if (targetFile.isFile()) {
targetFile.delete();
}
targetFile.mkdirs();
boolean result = true;
for (String file : assetFileList) {
String src = srcFilePath + File.separator + file;
String dest = destFilePath + File.separator + file;
result = result && copyAssetToSdCard(context, src, dest);
}
return result;
}
}
/**
* Copy single asset file to sdcard
*
* @param context android context
* @param srcFilePath src asset file path
* @param destFilePath dest sdcard file path
* @return true->success, false->failed.
*/
private static boolean copyAssetFileToSdCard(@Nullable Context context, @Nullable String srcFilePath, @Nullable String destFilePath) {
if (context == null || srcFilePath == null || destFilePath == null || srcFilePath.isEmpty() || destFilePath.isEmpty()) {
return false;
}
File targetFile = new File(destFilePath);
if (targetFile.exists()) return true;
AssetManager am = context.getAssets();
if (am == null) return false;
InputStream inputStream = null;
FileOutputStream outputStream = null;
try {
inputStream = am.open(srcFilePath);
outputStream = new FileOutputStream(destFilePath);
byte[] buffer = new byte[1024];
int length = inputStream.read(buffer);
while (length > 0) {
outputStream.write(buffer, 0, length);
length = inputStream.read(buffer);
}
outputStream.flush();
inputStream.close();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
targetFile.delete();
return false;
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
outputStream.getChannel().close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
/**
* Get asset file by assets folder name
*
* @param context android context
* @param assetFolder assets folder name
* @return asset file list with full path
*/
@NonNull
public static ArrayList<String> getAssetFilePathList(@Nullable Context context, @Nullable String assetFolder) {
ArrayList<String> fileList = new ArrayList<>();
if (context == null) return fileList;
AssetManager am = context.getAssets();
if (am == null) return fileList;
try {
String[] assetList = am.list(assetFolder);
fileList.addAll(Arrays.asList(assetList));
} catch (Exception e) {
e.printStackTrace();
}
return fileList;
}
}

View File

@@ -0,0 +1,73 @@
package com.alivc.live.commonutils;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Matrix;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* @author keria
* @date 2023/9/15
* @brief
*/
public class BitmapUtil {
private BitmapUtil() {
}
/**
* 文字生成bitmap
*
* @param context android context
* @param contents 文字内容
* @return bitmap图像
*/
public static Bitmap createTextBitmap(Context context, String contents) {
if (context == null || TextUtils.isEmpty(contents)) {
return null;
}
float scale = context.getResources().getDisplayMetrics().scaledDensity;
TextView tv = new TextView(context);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
tv.setLayoutParams(layoutParams);
tv.setText(contents);
tv.setTextSize(scale * 12);
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setDrawingCacheEnabled(true);
tv.setTextColor(Color.WHITE);
tv.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight());
tv.setBackgroundColor(Color.TRANSPARENT);
tv.buildDrawingCache();
return tv.getDrawingCache();
}
/**
* 翻转bitmap
*
* @param bitmap bitmap图像
* @param isFlipX X轴flip
* @param isFlipY Y轴flip
* @return 翻转后的bitmap
*/
public static Bitmap flipBitmap(Bitmap bitmap, boolean isFlipX, boolean isFlipY) {
if (bitmap == null || bitmap.isRecycled()) {
return null;
}
Matrix matrix = new Matrix();
matrix.setScale(isFlipX ? -1 : 1, isFlipY ? -1 : 1);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
}

View File

@@ -0,0 +1,34 @@
package com.alivc.live.commonutils;
import android.annotation.SuppressLint;
import android.content.Context;
import androidx.annotation.NonNull;
/**
* Created by keria on 2022/4/6.
*/
public class ContextUtils {
@SuppressLint("StaticFieldLeak")
private static Context sContext = null;
@SuppressLint("StaticFieldLeak")
private static Context sSafeContext = null;
public static void setContext(@NonNull Context context) {
sContext = context;
sSafeContext = new SafeToastContext(context);
}
public static Context getContext() {
return sContext;
}
public static Context getApplicationContext() {
return sContext;
}
public static Context getSafeToastContext() {
return sSafeContext;
}
}

View File

@@ -0,0 +1,104 @@
package com.alivc.live.commonutils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Build;
import android.view.Surface;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* @author keria
* @date 2022/4/19
*/
public class DeviceUtil {
private DeviceUtil() {
}
/**
* Get device brand
*
* @return brand name
*/
@NonNull
public static String getDeviceBrand() {
return Build.BRAND;
}
/**
* Get device os version
*
* @return device os version
*/
@NonNull
public static String getDeviceVersion() {
return Build.VERSION.RELEASE;
}
/**
* Get device os version code
*
* @return device os version code
*/
public static int getDeviceVersionCode() {
return Build.VERSION.SDK_INT;
}
/**
* Get device model
*
* @return device model name
*/
@NonNull
public static String getDeviceModel() {
return Build.MODEL;
}
/**
* Get device cpu arch
*
* @return device cpu arch name
*/
@SuppressLint("ObsoleteSdkInt")
@NonNull
public static String getDeviceCPUArch() {
return (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) ? Build.CPU_ABI : Build.SUPPORTED_ABIS[0];
}
/**
* Get device orientation
*
* @param context android context, nullable
* @return device orientation
*/
public static int getDisplayOrientation(@Nullable Context context) {
int displayOrientation = 0;
if (context == null) {
return displayOrientation;
}
int angle = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
switch (angle) {
case Surface.ROTATION_0:
displayOrientation = 0;
break;
case Surface.ROTATION_90:
displayOrientation = 90;
break;
case Surface.ROTATION_180:
displayOrientation = 180;
break;
case Surface.ROTATION_270:
displayOrientation = 270;
break;
default:
break;
}
return displayOrientation;
}
}

View File

@@ -0,0 +1,100 @@
package com.alivc.live.commonutils;
import android.app.DownloadManager;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
import static android.content.Context.DOWNLOAD_SERVICE;
/**
* 下载类
* 使用 DownloadManager 实现下载功能
*/
public class DownloadUtil {
private static DownloadManager mDownloadManager;
private OnDownloadListener mOnDownloadListener;
public DownloadUtil(Context context) {
if (mDownloadManager == null) {
mDownloadManager = (DownloadManager) context.getApplicationContext().getSystemService(DOWNLOAD_SERVICE);
}
}
public long startDownload(String url, File file) {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setDestinationUri(Uri.fromFile(file));
long downloadId = mDownloadManager.enqueue(request);
Timer timer = new Timer();
//检测下载进度和下载完成事件
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
DownloadManager.Query query = new DownloadManager.Query();
Cursor cursor = mDownloadManager.query(query.setFilterById(downloadId));
if (cursor != null && cursor.moveToFirst()) {
//获取当前下载状态
int columnIndexForStatus = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS);
switch (cursor.getInt(columnIndexForStatus)) {
case DownloadManager.STATUS_SUCCESSFUL:
if (mOnDownloadListener != null) {
mOnDownloadListener.onDownloadSuccess(downloadId);
}
cancel();
break;
case DownloadManager.ERROR_FILE_ALREADY_EXISTS:
if (mOnDownloadListener != null) {
mOnDownloadListener.onDownloadError(downloadId, DownloadManager.ERROR_FILE_ALREADY_EXISTS, "File already exists");
}
cancel();
break;
case DownloadManager.ERROR_HTTP_DATA_ERROR:
case DownloadManager.ERROR_FILE_ERROR:
if (mOnDownloadListener != null) {
mOnDownloadListener.onDownloadError(downloadId, DownloadManager.ERROR_HTTP_DATA_ERROR, "other error");
}
cancel();
break;
case DownloadManager.STATUS_RUNNING:
int columnIndexForDownloadBytes = cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
int columnIndexForTotalSize = cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);
//已下载大小
int downloadedBytes = cursor.getInt(columnIndexForDownloadBytes);
//总大小
int totalBytes = cursor.getInt(columnIndexForTotalSize);
double percent = (downloadedBytes * 100.00) / totalBytes;
if (mOnDownloadListener != null) {
mOnDownloadListener.onDownloadProgress(downloadId, Math.round(percent));
}
break;
}
cursor.close();
}
}
};
timer.schedule(timerTask, 0, 2000);
return downloadId;
}
public void cancelDownload(long id) {
mDownloadManager.remove(id);
}
public void setDownloadListener(OnDownloadListener downloadListener) {
this.mOnDownloadListener = downloadListener;
}
public interface OnDownloadListener {
void onDownloadSuccess(long downloadId);
void onDownloadProgress(long downloadId, double percent);
void onDownloadError(long downloadId, int errorCode, String errorMsg);
}
}

View File

@@ -0,0 +1,49 @@
package com.alivc.live.commonutils;
/**
* 快速点击判断
*
* @author ragnar
* @date 2019-08-14
*/
public class FastClickUtil {
private static final long PROCESS_MIN_DURATION = 300;
private static final long PROCESS_MIDDLE_DURATION = 450;
private static final long PROCESS_LONG_DURATION = 600;
private static long mLastProcessTime = 0;
/**
* 点击间隔时间是否在[PROCESS_MIN_DURATION]ms内
*/
public static boolean isProcessing() {
return isProcessing(PROCESS_MIN_DURATION);
}
/**
* 点击间隔时间是否在[PROCESS_MIDDLE_DURATION]ms内
*/
public static boolean isMiddleProcessing() {
return isProcessing(PROCESS_MIDDLE_DURATION);
}
/**
* 点击间隔时间是否在[PROCESS_LONG_DURATION]ms内
*/
public static boolean isLongProcessing() {
return isProcessing(PROCESS_LONG_DURATION);
}
private static boolean isProcessing(long pass) {
if (mLastProcessTime == 0L) {
mLastProcessTime = System.currentTimeMillis();
return false;
}
long currentTime = System.currentTimeMillis();
long passTime = currentTime - mLastProcessTime;
if (passTime < 0 || passTime > pass) {
mLastProcessTime = currentTime;
return false;
}
return true;
}
}

View File

@@ -0,0 +1,409 @@
package com.alivc.live.commonutils;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.util.ArrayList;
/**
* @author keria
* @date 2021/7/11
*/
public class FileUtil {
private FileUtil() {
}
/**
* Check if file exists
*
* @param path file path
* @return true->exists, false->not exist
*/
public static boolean fileExists(@Nullable String path) {
if (path == null) {
return false;
}
if (path.isEmpty()) {
return false;
}
File file = new File(path);
return file.exists();
}
/**
* Check if folder exists
*
* @param path folder path
* @return true->exists, false->not exist
*/
public static boolean folderExists(@Nullable String path) {
if (path == null) {
return false;
}
if (path.isEmpty()) {
return false;
}
File file = new File(path);
return file.exists() && file.isDirectory();
}
/**
* Get parent folder absolute path
*
* @param path file path
* @return parent folder absolute path
*/
@NonNull
public static String getFolderPath(@Nullable String path) {
if (path == null) {
return "";
}
if (path.isEmpty()) {
return "";
}
File file = new File(path);
String parent = file.getAbsoluteFile().getParent();
return parent != null ? parent : "";
}
/**
* Create folder if not exists
* <p>
* If the target is a file, remove it and then create folder.
*
* @param path folder path
*/
public static void safeCreateFolder(@Nullable String path) {
if (path == null || path.isEmpty()) {
return;
}
File file = new File(path);
if (file.isFile()) {
file.delete();
}
if (!file.exists()) {
file.mkdirs();
}
}
/**
* Remove file if exists
* <p>
* If the target exists, remove it
*
* @param path file path
*/
public static void safeDeleteFile(@Nullable String path) {
if (path == null || path.isEmpty()) {
return;
}
File file = new File(path);
if (file.exists()) {
file.delete();
}
}
/**
* Remove file if exists
* <p>
* If the target exists, remove it
*
* @param file file
*/
public static void safeDeleteFile(@Nullable File file) {
if (file == null || !file.exists()) {
return;
}
file.delete();
}
/**
* Get internal cache folder from sd card
*
* @param context android context
* @return internal cache folder from sdcard
*/
@NonNull
public static String getInternalCacheFolder(@Nullable Context context) {
if (context == null) {
return "";
}
return context.getCacheDir().getAbsolutePath();
}
/**
* Get internal files folder from sd card
*
* @param context android context
* @return internal files folder from sdcard
*/
public static String getInternalFilesFolder(@Nullable Context context) {
if (context == null) return "";
return context.getFilesDir().getAbsolutePath();
}
/**
* Get external cache folder from sd card
*
* @param context android context
* @return external cache folder from sdcard
*/
@NonNull
public static String getExternalCacheFolder(@Nullable Context context) {
if (context == null) return "";
return context.getExternalCacheDir().getAbsolutePath();
}
/**
* Get external files folder from sd card
*
* @param context android context
* @return external files folder from sdcard
*/
public static String getExternalFilesFolder(@Nullable Context context) {
if (context == null) return "";
return context.getExternalFilesDir(null).getAbsolutePath();
}
/**
* Combine paths
*
* @param varargs path list
* @return combined absolute path
*/
@NonNull
public static String combinePaths(String... varargs) {
if (varargs.length == 0) {
return "";
}
File parent = new File(varargs[0]);
int i = 1;
while (i < varargs.length) {
parent = new File(parent, varargs[i++]);
}
return parent.getAbsolutePath();
}
/**
* see this How-to for a faster way to convert
* a byte array to a HEX string
*
* @param filename file name
* @return md5 byte buffer
*/
@Nullable
public static byte[] createChecksum(@Nullable String filename) throws Exception {
if (!fileExists(filename)) {
return null;
}
InputStream fis = new FileInputStream(filename);
byte[] buffer = new byte[1024];
MessageDigest complete = MessageDigest.getInstance("MD5");
int numRead;
do {
numRead = fis.read(buffer);
if (numRead > 0) {
complete.update(buffer, 0, numRead);
}
} while (numRead != -1);
fis.close();
return complete.digest();
}
/**
* see this How-to for a faster way to convert
* a byte array to a HEX string
*
* @param filename file name
* @return md5 string
*/
@NonNull
public static String getMD5Checksum(@NonNull String filename) {
if (!fileExists(filename)) {
return "";
}
try {
byte[] b = createChecksum(filename);
if (b == null) {
return "";
}
StringBuilder result = new StringBuilder();
for (byte value : b) {
result.append(Integer.toString((value & 0xff) + 0x100, 16).substring(1));
}
return result.toString();
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
/**
* list all files
*
* @param file file folder
* @param includeFolder includeFolder
* @return all files
*/
@Nullable
public static ArrayList<File> listAllFiles(@Nullable File file, boolean includeFolder) {
if (file == null || !file.exists()) {
return null;
}
ArrayList<File> files = new ArrayList<>();
if (file.isFile()) {
files.add(file);
} else if (file.isDirectory()) {
if (includeFolder) {
files.add(file);
}
File[] filesArray = file.listFiles();
if (filesArray == null || file.length() == 0) {
return null;
}
for (File fileFolder : filesArray) {
ArrayList<File> subFiles = listAllFiles(fileFolder, includeFolder);
if (subFiles == null || subFiles.isEmpty()) {
continue;
}
files.addAll(subFiles);
}
}
return files;
}
/**
* read lines from file
*
* @param file file
* @return file lines
*/
@Nullable
public static ArrayList<String> readLinesFromFile(@Nullable File file) {
if (file == null || !file.exists() || !file.canRead()) {
return null;
}
FileReader fileReader = null;
try {
fileReader = new FileReader(file.getAbsolutePath());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
if (fileReader == null) {
return null;
}
ArrayList<String> lines = new ArrayList<>();
BufferedReader bufferedReader = new BufferedReader(fileReader, 1000 * 8192);
try {
String st;
while ((st = bufferedReader.readLine()) != null) {
lines.add(st);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return lines;
}
@Nullable
public static String readTextFromFile(@Nullable File file) {
if (file == null || !file.exists() || !file.canRead()) {
return null;
}
FileReader fileReader = null;
try {
fileReader = new FileReader(file.getAbsolutePath());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
if (fileReader == null) {
return null;
}
StringBuilder sb = new StringBuilder();
BufferedReader bufferedReader = new BufferedReader(fileReader, 1000 * 8192);
try {
String st;
while ((st = bufferedReader.readLine()) != null) {
sb.append(st).append('\n');
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
public static String getFileMD5(File file) {
if (file == null) {
return "";
}
String value = null;
FileInputStream in = null;
try {
in = new FileInputStream(file);
MappedByteBuffer byteBuffer = in.getChannel().map(FileChannel.MapMode.READ_ONLY, 0,
file.length());
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(byteBuffer);
BigInteger bi = new BigInteger(1, md5.digest());
value = bi.toString(16);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != in) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return value;
}
}

View File

@@ -0,0 +1,55 @@
package com.alivc.live.commonutils;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class HttpUtils {
private static final OkHttpClient mOkHttpClient = new OkHttpClient.Builder()
.connectTimeout(50000, TimeUnit.MILLISECONDS)
.readTimeout(50000, TimeUnit.MILLISECONDS)
.build();
public static void get(String url, OnNetWorkListener listener) {
Request okHttpRequest = new Request.Builder()
.url(url)
.head()
.build();
mOkHttpClient.newCall(okHttpRequest).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
if (listener != null) {
listener.onError();
}
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) {
if (listener != null) {
if (response.isSuccessful()) {
listener.onSuccess(response);
} else {
listener.onFailure(response.code(), response.message());
}
}
}
});
}
public interface OnNetWorkListener {
void onSuccess(Object obj);
void onFailure(int code, String msg);
void onError();
}
}

View File

@@ -0,0 +1,232 @@
package com.alivc.live.commonutils;
import android.content.Context;
import android.util.Log;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Date;
public class LogcatHelper {
private static LogcatHelper INSTANCE = null;
private static String PATH_LOGCAT;
private static String PATH_DIR = "LivePush";
private LogDumper mLogDumper = null;
private int mPId;
private void init(Context context) {
PATH_LOGCAT = context.getFilesDir().getAbsolutePath()
+ File.separator + PATH_DIR;
File file = new File(PATH_LOGCAT);
if (!file.exists()) {
file.mkdirs();
}
}
public static LogcatHelper getInstance(Context context) {
if (INSTANCE == null) {
INSTANCE = new LogcatHelper(context);
}
return INSTANCE;
}
private LogcatHelper(Context context) {
init(context);
mPId = android.os.Process.myPid();
}
public void start() {
autoClear(PATH_LOGCAT);
if (mLogDumper == null)
mLogDumper = new LogDumper(String.valueOf(mPId), PATH_LOGCAT);
Log.d("LogcatHelper", "logcat thread " + mLogDumper.isAlive());
if(!mLogDumper.isAlive()) {
try {
mLogDumper.start();
} catch (IllegalThreadStateException e) {
Log.e("LogcatHelper", "thread already started");
}
}
}
public void stop() {
if (mLogDumper != null) {
mLogDumper.stopLogs();
mLogDumper = null;
}
}
private class LogDumper extends Thread {
private Process logcatProc;
private BufferedReader mReader = null;
private boolean mRunning = true;
String cmds = null;
private String mPID;
private FileOutputStream out = null;
public LogDumper(String pid, String dir) {
mPID = pid;
try {
out = new FileOutputStream(new File(dir, PATH_DIR + "-"
+ LogcatDate.getFileName()+ ".log"), true);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// cmds = "logcat *:e *:w | grep \"(" + mPID + ")\"";
cmds = "logcat | grep \"(" + mPID + ")\"";//打印所有日志信息
//cmds = "logcat";//打印所有日志信息
// cmds = "logcat -s way";//打印标签过滤信息
//cmds = "logcat *:e *:i | grep \"(" + mPID + ")\"";
}
public void stopLogs() {
mRunning = false;
}
@Override
public void run() {
try {
logcatProc = Runtime.getRuntime().exec(cmds);
mReader = new BufferedReader(new InputStreamReader(
logcatProc.getInputStream()), 1024);
String line = null;
while (mRunning && (line = mReader.readLine()) != null) {
if (!mRunning) {
break;
}
if (line.length() == 0) {
continue;
}
if(isCreatNewFile(new File(PATH_LOGCAT, PATH_DIR + "-"
+ LogcatDate.getFileName()+ ".log"))) {
new File(PATH_LOGCAT, PATH_DIR + "-"
+ LogcatDate.getFileName()+ ".log").renameTo(new File(PATH_LOGCAT, PATH_DIR + "-"
+ LogcatDate.getDateEN()+ ".log"));
try {
out = new FileOutputStream(new File(PATH_LOGCAT, PATH_DIR + "-"
+ LogcatDate.getFileName()+ ".log"), true);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (out != null && line.contains(mPID)) {
out.write((LogcatDate.getDateEN() + " " + line + "\n")
.getBytes());
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (logcatProc != null) {
logcatProc.destroy();
logcatProc = null;
}
if (mReader != null) {
try {
mReader.close();
mReader = null;
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
out = null;
}
}
}
}
private boolean isCreatNewFile(File file) {
boolean bool = false;
FileInputStream inputStream = null;
if(file.exists()) {
try {
inputStream = new FileInputStream(file);
} catch (FileNotFoundException e) {
//e.printStackTrace();
return false;
}
}
if(inputStream != null) {
try {
if(inputStream.available()/1024.f/1024.f > 10) {
bool = true;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return bool;
}
private void autoClear(String dir) {
if(new File(dir).exists() && new File(dir).isDirectory()) {
File[] files = new File(dir).listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
//大于2天
if((System.currentTimeMillis() - file.lastModified()) / 1000 > 24 * 60 * 60 * 2) {
return true;
}
return false;
}
});
if(files != null && files.length > 0) {
for(File file : files) {
file.delete();
}
}
}
}
public static class LogcatDate {
public static String getFileName() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
String date = format.format(new Date(System.currentTimeMillis()));
return date;
}
public static String getDateEN() {
SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
String date1 = format1.format(new Date(System.currentTimeMillis()));
return date1;
}
}
}

View File

@@ -0,0 +1,121 @@
package com.alivc.live.commonutils;
import android.content.Context;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.telephony.TelephonyManager;
public class NetWorkUtils {
/**
* 判断是否有网络连接
*
* @param context
* @return
*/
public static boolean isNetworkConnected(Context context) {
if (context != null) {
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
if (networkInfo != null)
return networkInfo.isAvailable();
}
return false;
}
/**
* 判断WIFI网络是否可用
*
* @param context
* @return
*/
public static boolean isWifiConnected(Context context) {
if (context != null) {
// 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理)
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
// 获取NetworkInfo对象
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
//判断NetworkInfo对象是否为空 并且类型是否为WIFI
if (networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI)
return networkInfo.isAvailable();
}
return false;
}
/**
* 判断MOBILE网络是否可用
*
* @param context
* @return
*/
public static boolean isMobileConnected(Context context) {
if (context != null) {
//获取手机所有连接管理对象(包括对wi-fi,net等连接的管理)
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
//获取NetworkInfo对象
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
//判断NetworkInfo对象是否为空 并且类型是否为MOBILE
if (networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_MOBILE)
return networkInfo.isAvailable();
}
return false;
}
public static int getConnectedType(Context context) {
if (context != null) {
//获取手机所有连接管理对象
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
//获取NetworkInfo对象
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isAvailable()) {
//返回NetworkInfo的类型
return networkInfo.getType();
}
}
return -1;
}
/**
* 获取当前的网络状态 :没有网络-0WIFI网络14G网络-43G网络-32G网络-2
* 自定义
*
* @param context
* @return
*/
public static int getAPNType(Context context) {
int netType = 0;
ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = manager.getActiveNetworkInfo();
//NetworkInfo对象为空 则代表没有网络
if (networkInfo == null) {
return netType;
}
int nType = networkInfo.getType();
if (nType == ConnectivityManager.TYPE_WIFI) {
//WIFI
netType = 1;
} else if (nType == ConnectivityManager.TYPE_MOBILE) {
int nSubType = networkInfo.getSubtype();
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
//3G 联通的3G为UMTS或HSDPA 电信的3G为EVDO
if (nSubType == TelephonyManager.NETWORK_TYPE_LTE
&& !telephonyManager.isNetworkRoaming()) {
netType = 4;
} else if (nSubType == TelephonyManager.NETWORK_TYPE_UMTS
|| nSubType == TelephonyManager.NETWORK_TYPE_HSDPA
|| nSubType == TelephonyManager.NETWORK_TYPE_EVDO_0
&& !telephonyManager.isNetworkRoaming()) {
netType = 3;
//2G 移动和联通的2G为GPRS或EGDE电信的2G为CDMA
} else if (nSubType == TelephonyManager.NETWORK_TYPE_GPRS
|| nSubType == TelephonyManager.NETWORK_TYPE_EDGE
|| nSubType == TelephonyManager.NETWORK_TYPE_CDMA
&& !telephonyManager.isNetworkRoaming()) {
netType = 2;
} else {
netType = 2;
}
}
return netType;
}
}

View File

@@ -0,0 +1,83 @@
package com.alivc.live.commonutils;
import android.content.Context;
import android.content.ContextWrapper;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import androidx.annotation.NonNull;
/**
* Created by keria on 2022/4/6.
*/
final class SafeToastContext extends ContextWrapper {
SafeToastContext(@NonNull Context base) {
super(base);
}
@Override
public Context getApplicationContext() {
return new ApplicationContextWrapper(getBaseContext().getApplicationContext());
}
private static final class ApplicationContextWrapper extends ContextWrapper {
private ApplicationContextWrapper(@NonNull Context base) {
super(base);
}
@Override
public Object getSystemService(@NonNull String name) {
if (Context.WINDOW_SERVICE.equals(name)) {
return new WindowManagerWrapper((WindowManager) getBaseContext().getSystemService(name));
}
return super.getSystemService(name);
}
}
private static final class WindowManagerWrapper implements WindowManager {
private static final String TAG = "WindowManagerWrapper";
@NonNull
private final WindowManager base;
private WindowManagerWrapper(@NonNull WindowManager base) {
this.base = base;
}
@Override
public Display getDefaultDisplay() {
return base.getDefaultDisplay();
}
@Override
public void removeViewImmediate(View view) {
base.removeViewImmediate(view);
}
@Override
public void addView(View view, ViewGroup.LayoutParams params) {
try {
Log.d(TAG, "WindowManager's addView(view, params) has been hooked.");
base.addView(view, params);
} catch (BadTokenException e) {
Log.i(TAG, e.getMessage());
} catch (Throwable throwable) {
Log.e(TAG, throwable.toString());
}
}
@Override
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
base.updateViewLayout(view, params);
}
@Override
public void removeView(View view) {
base.removeView(view);
}
}
}

View File

@@ -0,0 +1,105 @@
package com.alivc.live.commonutils;
import android.content.Context;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
/**
* Created by keria on 2022/10/12.
* <p>
* Android数据持久化
*/
public class SharedPrefUtils {
private static final String PREF_APP = "livepush";
private SharedPrefUtils() {
}
/**
* Gets boolean data.
*
* @param context the context
* @param key the key
* @param val default value
* @return the boolean data
*/
public static boolean getBooleanData(@NonNull Context context, String key, boolean val) {
return getSharedPref(context).getBoolean(key, val);
}
/**
* Gets int data.
*
* @param context the context
* @param key the key
* @param val default value
* @return the int data
*/
public static int getIntData(@NonNull Context context, String key, int val) {
return getSharedPref(context).getInt(key, val);
}
/**
* Gets string data.
*
* @param context the context
* @param key the key
* @param val default value
* @return the string data
*/
public static String getStringData(@NonNull Context context, String key, String val) {
return getSharedPref(context).getString(key, val);
}
/**
* Save data.
*
* @param context the context
* @param key the key
* @param val default value
*/
public static void saveData(@NonNull Context context, String key, String val) {
getSharedPrefEditor(context).putString(key, val).apply();
}
/**
* Save data.
*
* @param context the context
* @param key the key
* @param val default value
*/
public static void saveData(@NonNull Context context, String key, int val) {
getSharedPrefEditor(context).putInt(key, val).apply();
}
/**
* Save data.
*
* @param context the context
* @param key the key
* @param val default value
*/
public static void saveData(@NonNull Context context, String key, boolean val) {
getSharedPrefEditor(context).putBoolean(key, val).apply();
}
/**
* Clear all data
*
* @param context the context
*/
public static void clear(@NonNull Context context) {
getSharedPrefEditor(context).clear();
}
private static SharedPreferences getSharedPref(@NonNull Context context) {
return context.getSharedPreferences(PREF_APP, Context.MODE_PRIVATE);
}
private static SharedPreferences.Editor getSharedPrefEditor(@NonNull Context context) {
return getSharedPref(context).edit();
}
}

View File

@@ -0,0 +1,45 @@
package com.alivc.live.commonutils;
import android.content.Context;
import android.text.TextUtils;
public class TextFormatUtil {
public static final String REGULAR = "[0-9a-zA-Z]{1,20}";
public static String getTextFormat(Context context, int id, Object... o) {
String s = context.getResources().getString(id);
return String.format(s, o);
}
public static String getTextFormat(Context context, String paramString, Object... o) {
int id = context.getResources().getIdentifier(paramString, "string", context.getPackageName());
String s = context.getResources().getString(id);
return String.format(s, o);
}
public static String getTextFormat(Context context, int id) {
return context.getResources().getString(id);
}
public static String[] getTextArray(Context context, int id) {
return context.getResources().getStringArray(id);
}
public static int[] getIntArray(Context context, int id) {
return context.getResources().getIntArray(id);
}
public static int convertString2Int(String str) {
if (TextUtils.isEmpty(str)) {
return -1;
}
int number = -1;
try {
number = Integer.parseInt(str.trim());
} catch (NumberFormatException e) {
e.printStackTrace();
}
return number;
}
}

View File

@@ -0,0 +1,22 @@
package com.alivc.live.commonutils;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.widget.Toast;
public class ToastUtils {
public static void show(final String content) {
if (!TextUtils.isEmpty(content)) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast.makeText(ContextUtils.getSafeToastContext(), content, Toast.LENGTH_SHORT).show();
}
});
}
}
}

View File

@@ -0,0 +1,326 @@
package com.alivc.live.commonutils.testexecutor;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author baorunchen
* @date 2019/7/10
* @brief 通过调度和执行任务来测试 API 在多线程、高并发场景下的可靠性
* @note 每秒指定次数地切换线程执行任务,并随机在主线程上执行任务
*/
public class TestScheduledExecutor {
private static final String TAG = "TestScheduledExecutor";
private static final int TASK_RETRY_COUNT = 3; // 可配置重试次数
private static final int THREAD_POOL_SIZE = 8; // 线程池大小
private static final long SHUTDOWN_TIMEOUT = 1000; // 线程池关闭等待时间,毫秒
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final ExecutorService[] threadPools = new ExecutorService[THREAD_POOL_SIZE];
private final AtomicInteger currentPoolIndex = new AtomicInteger(0);
private final Handler mainHandler = new Handler(Looper.getMainLooper());
private final Random random = new Random();
private final int taskInterval;
private final Strategy strategy;
private final PriorityBlockingQueue<PriorityTask> priorityQueue = new PriorityBlockingQueue<>();
public enum Strategy {
DEFAULT,
PRIORITY,
RETRY,
TIMEOUT,
DELAY
}
/**
* 公共构造函数,初始化并启动任务调度
*
* @param tasks 要执行的任务列表
* @param taskInterval 执行时间间隔(毫秒)
* @param strategy 启用的策略
*/
public TestScheduledExecutor(final List<Runnable> tasks, int taskInterval, Strategy strategy) {
this.taskInterval = taskInterval;
this.strategy = strategy;
// 初始化多个线程池
for (int i = 0; i < THREAD_POOL_SIZE; i++) {
threadPools[i] = Executors.newFixedThreadPool(1, new CustomThreadFactory("ThreadPool-" + (i + 1) + "-Thread-"));
}
// 调度任务:按照指定时间间隔执行
scheduler.scheduleAtFixedRate(() -> {
int index = currentPoolIndex.getAndIncrement() % THREAD_POOL_SIZE;
Runnable selectedTask = tasks.get(random.nextInt(tasks.size())); // 随机选择一个任务
Runnable wrappedTask = wrapWithStrategy(selectedTask);
RunnableTask task = new RunnableTask(wrappedTask);
if (strategy == Strategy.PRIORITY) {
int priority = random.nextInt(100); // 设置任务的优先级
priorityQueue.add(new PriorityTask(priority, task));
executePriorityTask();
} else {
executeTask(index, task, random.nextBoolean());
}
}, 0, taskInterval, TimeUnit.MILLISECONDS);
}
/**
* 公共构造函数,初始化并启动任务调度
*
* @param tasks 要执行的任务列表
* @param taskInterval 执行时间间隔(毫秒)
*/
public TestScheduledExecutor(final List<Runnable> tasks, int taskInterval) {
this(tasks, taskInterval, Strategy.DEFAULT);
}
/**
* 只接受单个Runnable的构造函数
*
* @param runnable 要执行的任务
* @param taskInterval 执行时间间隔(毫秒)
*/
public TestScheduledExecutor(Runnable runnable, int taskInterval) {
this(Collections.singletonList(runnable), taskInterval, Strategy.DEFAULT);
}
/**
* 只接受单个Runnable的构造函数
*
* @param runnable 要执行的任务
* @param taskInterval 执行时间间隔(毫秒)
* @param strategy 启用的策略
*/
public TestScheduledExecutor(Runnable runnable, int taskInterval, Strategy strategy) {
this(Collections.singletonList(runnable), taskInterval, strategy);
}
private Runnable wrapWithStrategy(Runnable task) {
switch (strategy) {
case DELAY:
int delay = random.nextInt(taskInterval * 2);
return new DelayTask(task, delay);
case RETRY:
return new RetryTask(task, TASK_RETRY_COUNT);
case TIMEOUT:
return new TimeoutTask(task, taskInterval * 2L);
case PRIORITY:
// 优先级调度在调度线程处理中
return task;
default:
return task;
}
}
private void executePriorityTask() {
if (!priorityQueue.isEmpty()) {
PriorityTask priorityTask = priorityQueue.poll();
if (priorityTask != null) {
int index = currentPoolIndex.getAndIncrement() % THREAD_POOL_SIZE;
executeTask(index, priorityTask, priorityTask.isMainThread);
}
}
}
private void executeTask(int index, Runnable task, boolean isMainThread) {
if (isMainThread) {
mainHandler.post(task);
} else {
threadPools[index].execute(task);
}
}
/**
* 销毁并清理资源
*/
public void destroy() {
shutdownExecutor(scheduler, "Scheduler");
for (ExecutorService threadPool : threadPools) {
shutdownExecutor(threadPool, "ThreadPool");
}
}
/**
* 封装的关闭 Executor 服务的方法
*
* @param executor 要关闭的 Executor 服务
* @param name Executor 服务的名称(用于日志)
*/
private static void shutdownExecutor(ExecutorService executor, String name) {
if (executor == null) return;
executor.shutdown(); // 禁止提交新任务
try {
// 首先试图优雅地关闭
if (!executor.awaitTermination(SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS)) {
executor.shutdownNow(); // 如果优雅关闭超时,则强制关闭
if (!executor.awaitTermination(SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS)) {
Log.e(TAG, name + " did not terminate");
}
}
} catch (InterruptedException e) {
executor.shutdownNow(); // 响应中断以强制关闭
Thread.currentThread().interrupt(); // 恢复中断状态
Log.e(TAG, "Interrupted while shutting down " + name, e);
}
}
/**
* 自定义线程工厂,用于设置线程的名称和属性
*/
private static class CustomThreadFactory implements ThreadFactory {
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
CustomThreadFactory(String namePrefix) {
this.namePrefix = namePrefix;
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName(namePrefix + threadNumber.getAndIncrement());
t.setDaemon(false);
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
/**
* 自定义Runnable任务类
*/
private static class RunnableTask implements Runnable {
private final Runnable runnable;
RunnableTask(Runnable runnable) {
this.runnable = runnable;
}
@Override
public void run() {
Log.d(TAG, "Executing task in " + Thread.currentThread().getName());
runnable.run();
}
}
/**
* 任务延迟策略
*/
private static class DelayTask implements Runnable {
private final Runnable task;
private final int delay;
public DelayTask(Runnable task, int delay) {
this.task = task;
this.delay = delay;
}
@Override
public void run() {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.e(TAG, "Task interrupted in delay", e);
}
task.run();
}
}
/**
* 任务重试策略
*/
private static class RetryTask implements Runnable {
private final Runnable task;
private final int maxRetries;
public RetryTask(Runnable task, int maxRetries) {
this.task = task;
this.maxRetries = maxRetries;
}
@Override
public void run() {
int retryCount = 0;
while (retryCount < maxRetries) {
try {
task.run();
break; // 成功执行后退出循环
} catch (Exception e) {
retryCount++;
if (retryCount >= maxRetries) {
Log.e(TAG, "Task failed after " + maxRetries + " retries", e);
}
}
}
}
}
/**
* 任务超时策略
*/
private static class TimeoutTask implements Runnable {
private final Runnable task;
private final long timeout;
public TimeoutTask(Runnable task, long timeout) {
this.task = task;
this.timeout = timeout;
}
@Override
public void run() {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<?> future = executor.submit(task);
try {
future.get(timeout, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
future.cancel(true);
Log.e(TAG, "Task timed out", e);
} catch (Exception e) {
Log.e(TAG, "Task execution failed", e);
} finally {
executor.shutdown();
}
}
}
/**
* 任务优先级策略
*/
private static class PriorityTask implements Runnable, Comparable<PriorityTask> {
private final int priority;
private final Runnable task;
private final boolean isMainThread;
public PriorityTask(int priority, Runnable task) {
this(priority, task, false);
}
public PriorityTask(int priority, Runnable task, boolean isMainThread) {
this.priority = priority;
this.task = task;
this.isMainThread = isMainThread;
}
@Override
public void run() {
task.run();
}
@Override
public int compareTo(PriorityTask other) {
return Integer.compare(other.priority, this.priority); // 高优先级任务优先
}
}
}

View File

@@ -0,0 +1,239 @@
package com.alivc.live.commonutils.viewhierarchy;
import android.graphics.Rect;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author baorunchen
* @date 2024/5/16
* @brief Monitor changes in view hierarchy and regularly print view hierarchy and overlaps
*/
/****
* @par Call Example
* @code
* private ViewHierarchyInspector mViewHierarchyInspector;
*
* @Override protected void onCreate(Bundle savedInstanceState) {
* super.onCreate(savedInstanceState);
*
* // xxx;
*
* mViewHierarchyInspector = new ViewHierarchyInspector();
* mViewHierarchyInspector.setTargetView(mOwnerFrameLayout);
* mViewHierarchyInspector.setDecorView(getWindow().getDecorView());
* }
*
* @Override protected void onDestroy() {
* super.onDestroy();
*
* // xxx;
*
* if (mViewHierarchyInspector != null) {
* mViewHierarchyInspector.destroy();
* mViewHierarchyInspector = null;
* }
* }
* @endcode
*/
public class ViewHierarchyInspector {
private static final String TAG = "ViewHierarchyInspector";
private static final long SCHEDULED_EXECUTOR_SERVICE_PERIOD = 2 * 1000L;
private ScheduledExecutorService mScheduledExecutorService = null;
private View mTargetView = null;
private View mDecorView = null;
/**
* Sets the target view to inspect and starts the periodic inspection timer.
* It logs the reference of the view assigned.
*
* @param targetView The target view to be inspected.
*/
public void setTargetView(View targetView) {
Log.w(TAG, "[UI] setTargetView: [" + targetView + "]");
mTargetView = targetView;
startTimer();
}
/**
* Sets the root view (decorView) from which the view hierarchy will be inspected.
* It logs the reference of the view assigned.
*
* @param decorView The decorView (root view) of the Activity or Window.
*/
public void setDecorView(View decorView) {
Log.w(TAG, "[UI] setDecorView: [" + decorView + "]");
mDecorView = decorView;
}
/**
* Stops the inspection timer and releases the references to the view and decorView.
* This should be called to clean up when the inspector is no longer needed.
*/
public void destroy() {
stopTimer();
mTargetView = null;
mDecorView = null;
}
/**
* Starts the traversal of the view tree to print the view hierarchy and check for overlaps.
*/
private void startInspector(View targetView) {
if (mTargetView != null) {
Log.i(TAG, "[UI][INSPECTOR][TRAVERSE]: ---------- " + targetView + " ----------");
traverseViewTree(targetView);
}
if (mTargetView != null && mDecorView != null) {
Log.i(TAG, "[UI][INSPECTOR][OVERLAP]: ---------- " + mDecorView + " ----------");
printViewHierarchyWithOverlap(mDecorView, mTargetView);
}
}
/**
* Recursively traverses a view tree, Logging information about each child view.
* This method should be used for debugging to understand the structure of a view tree.
*
* @param targetView The root of the view tree to traverse.
*/
private static void traverseViewTree(View targetView) {
if (targetView instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) targetView;
Log.i(TAG, "[UI][TRAVERSE][VIEW_GROUP]: [" + viewGroup
+ "], Children: [" + viewGroup.getChildCount() + "]");
for (int i = 0; i < viewGroup.getChildCount(); i++) {
View childView = viewGroup.getChildAt(i);
traverseViewTree(childView);
Log.i(TAG, "[UI][TRAVERSE][CHILD]: [" + childView + "]");
}
} else {
Log.i(TAG, "[UI][TRAVERSE][VIEW]: [" + targetView + "]");
}
}
/**
* Prints a single-line representation of the view hierarchy starting from the root view,
* marking the target view and any overlapping views.
*
* @param rootView The root view from which to start the traversal.
* @param targetView The view to mark and check for overlaps.
*/
private static void printViewHierarchyWithOverlap(View rootView, View targetView) {
StringBuilder builder = new StringBuilder();
Rect targetRect = new Rect();
targetView.getGlobalVisibleRect(targetRect); // Get the global position of the target view.
buildViewHierarchyStringWithOverlap(rootView, targetView, targetRect, 0, builder);
// Print the built hierarchy string
Log.i(TAG, "[UI][HIERARCHY][ " + builder + "]");
}
/**
* Recursively traverses the view hierarchy, checking for overlap with the target view
* and appending to the StringBuilder instance.
*
* @param rootView The current view being inspected.
* @param targetView The target view to mark and check for overlaps.
* @param targetRect The global visible rectangle of the target view for overlap testing.
* @param depth The depth in the view hierarchy.
* @param builder The StringBuilder used to build the output string.
*/
private static void buildViewHierarchyStringWithOverlap(View rootView, View targetView, Rect targetRect, int depth, StringBuilder builder) {
builder.append("\n|");
// Append dashes to indicate the depth of the view in the hierarchy
for (int i = 0; i < depth; i++) {
builder.append("-");
}
builder.append("[").append(depth).append("]");
// Append visibility status of the view.
String visibilityStatus = getVisibilityStatus(rootView);
builder.append("[").append(visibilityStatus).append("]");
if (rootView == targetView) {
builder.append("[!TARGET]");
}
// Check for overlap and append the overlap marker if needed
Rect viewRect = new Rect();
rootView.getGlobalVisibleRect(viewRect);
if (Rect.intersects(viewRect, targetRect) && rootView != targetView) {
builder.append("[!OVERLAP]");
}
builder.append(rootView);
// If the view is a ViewGroup, recursively process its children
if (rootView instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) rootView;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
buildViewHierarchyStringWithOverlap(viewGroup.getChildAt(i), targetView, targetRect, depth + 1, builder);
}
}
}
/**
* Gets the visibility status of a view as a string.
*
* @param view The view whose visibility status is to be determined.
* @return A string representing the visibility status of the view.
*/
private static String getVisibilityStatus(View view) {
switch (view.getVisibility()) {
case View.VISIBLE:
return "VISIBLE";
case View.INVISIBLE:
return "INVISIBLE";
case View.GONE:
return "GONE";
default:
return "UNKNOWN";
}
}
/**
* Starts a scheduled executor service with a fixed delay to periodically inspect the view hierarchy.
* The inspection looks for overlapping views in the hierarchy and logs them.
*/
private void startTimer() {
stopTimer();
mScheduledExecutorService = Executors.newScheduledThreadPool(8);
mScheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
startInspector(mTargetView);
}
}, 0, SCHEDULED_EXECUTOR_SERVICE_PERIOD, TimeUnit.MILLISECONDS);
}
/**
* Stops the scheduled executor service and waits for its termination.
* If the service doesn't terminate within the specified timeout, it's shut down immediately.
*/
private void stopTimer() {
try {
if (mScheduledExecutorService != null) {
mScheduledExecutorService.shutdown();
if (!mScheduledExecutorService.awaitTermination(1000, TimeUnit.MICROSECONDS)) {
mScheduledExecutorService.shutdownNow();
}
mScheduledExecutorService = null;
}
} catch (InterruptedException e) {
if (mScheduledExecutorService != null) {
mScheduledExecutorService.shutdownNow();
}
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
<color name="thumbGradient">#eeeeee</color>
<color name="trackGradient">#888888</color>
<color name="red">#ffff0000</color>
<color name="darker_gray">#aaa</color>
<color name="text_blue">#2d44f4</color>
<color name="yellow">#ffff00</color>
<color name="transparent">#50000000</color>
<color name="text_green">#00c0fb</color>
<color name="text_black">#333333</color>
<color name="wheel_white">#FFFFFF</color>
<color name="alivc_version_color">#8C8C8C</color>
<color name="wheel_text_color_2">#666666</color>
<color name="alivc_color_gry">#e0e0e0</color>
<color name="wheel_text_color_1">#333333</color>
<color name="alivc_color_black">#000</color>
<color name="color_bg_model">#F9F9F9</color>
<color name="text_color_model_name">#161A23</color>
<color name="color_bg_border_model">#dde0e5</color>
<color name="color_background_green">#1AED99</color>
<color name="color_background_black_alpha_30">#3323262F</color>
<color name="color_title_text_black">#111111</color>
<color name="color_background_white">#ffffffff</color>
<color name="wheel_black">#000000</color>
<color name="color_text_white">#fff</color>
<color name="color_text_grey">#A1A1A1</color>
<color name="grey_bg_color">#F4F4F4</color>
<color name="title_bg_grey_color">#FAFAFA</color>
<color name="switch_bar_on_color">#365AFF</color>
<color name="tips_color">#D93026</color>
<color name="title_color">#555555</color>
<color name="color_07C2DE">#07C2DE</color>
<color name="shape_red_rectangle">#F53F3F</color>
<color name="border_medium">#3A3D48</color>
<color name="text_strong">#FCFCFD</color>
<color name="fill_weak">#23262F</color>
<color name="colourful_border_weak">#B2EBF2</color>
<color name="bg_weak">#1C1D22</color>
<color name="text_medium">#E6E7EC</color>
<color name="text_ultraweak">#747A8C</color>
<color name="colourful_ic_strong">#4DCFE1</color>
<color name="border_infrared">#23262F</color>
<color name="ic_ultraweak">#3A3D48</color>
<color name="text_weak">#B2B7C4</color>
<color name="colourful_text_strong">#4DCFE1</color>
<color name="push_btn_border_color">#EDEDED</color>
<color name="body_txt_color">#ff16243c</color>
<color name="text_true_color">#FF6500</color>
<color name="main_color">#FFFFFF</color>
<color name="sound_effect_background">#DDDDDD</color>
</resources>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="font_size_40px">20.0sp</dimen>
<dimen name="font_size_36px">18.0sp</dimen>
<dimen name="font_size_32px">16.0sp</dimen>
<dimen name="font_size_30px">15.0sp</dimen>
<dimen name="font_size_28px">14.0sp</dimen>
<dimen name="font_size_24px">12.0sp</dimen>
<dimen name="view_line_width">0.1dp</dimen>
<dimen name="view_size_text_14">14dp</dimen>
<dimen name="view_size_text_16">16dp</dimen>
<dimen name="view_size_text_17">17dp</dimen>
<dimen name="view_margin_36">36dp</dimen>
<dimen name="view_margin_2">2dp</dimen>
<dimen name="view_margin_8">8dp</dimen>
<dimen name="view_margin_9">9dp</dimen>
<dimen name="view_margin_10">10dp</dimen>
<dimen name="view_margin_12">12dp</dimen>
<dimen name="view_margin_15">15dp</dimen>
<dimen name="view_margin_18">18dp</dimen>
<dimen name="view_margin_20">20dp</dimen>
<dimen name="view_margin_22">22dp</dimen>
<dimen name="view_margin_24">24dp</dimen>
<dimen name="view_margin_25">25dp</dimen>
<dimen name="view_margin_30">30dp</dimen>
<dimen name="view_margin_40">40dp</dimen>
<dimen name="view_margin_65">65dp</dimen>
<dimen name="view_margin_75">75dp</dimen>
<dimen name="view_margin_85">85dp</dimen>
<dimen name="view_margin_110">110dp</dimen>
<dimen name="view_margin_140">140dp</dimen>
<dimen name="view_margin_261">261dp</dimen>
<dimen name="view_margin_310">310dp</dimen>
<dimen name="view_margin_330">330dp</dimen>
<dimen name="view_width_size_64">64dp</dimen>
<dimen name="view_height_size_32">32dp</dimen>
<dimen name="view_height_size_36">36dp</dimen>
<dimen name="view_height_size_44">44dp</dimen>
<dimen name="view_height_size_45">45dp</dimen>
<dimen name="view_version_size_text_12">12dp</dimen>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>