Commit 079c6cb0 authored by 王雪伟's avatar 王雪伟

Initial commit

parents
Pipeline #760 failed with stages

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

# Java class files
*.class
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
.idea/caches
.gradle
.idea/
gradlew.bat
build/
/gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
cms/build
cms/*.iml
app/build
app/debug
app/*.iml
moduleMain/build
moduleMain/*.iml
moduleUc/build
moduleUc/*.iml
moduleVideo/build
moduleVideo/*.iml
\ No newline at end of file
apply plugin: 'com.android.library'
android {
compileSdkVersion COMPILE_SDK_VERSION as Integer
buildToolsVersion BUILD_TOOLS_VERSION
defaultConfig {
minSdkVersion MIN_SDK_VERSION as Integer
targetSdkVersion TARGET_SDK_VERSION as Integer
versionName VERSION_NAME
versionCode VERSION_CODE as Integer
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
api fileTree(include: ['*.jar'], dir: 'libs')
}
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
<manifest package="com.tencent.liteav.debug">
<application></application>
</manifest>
\ No newline at end of file
package com.tencent.liteav.debug;
import android.text.TextUtils;
import android.util.Base64;
import org.json.JSONException;
import org.json.JSONObject;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.zip.Deflater;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
/*
* Module: GenerateTestUserSig
*
* Function: 用于生成测试用的 UserSig,UserSig 是腾讯云为其云服务设计的一种安全保护签名。
* 其计算方法是对 SDKAppID、UserID 和 EXPIRETIME 进行加密,加密算法为 HMAC-SHA256。
*
* Attention: 请不要将如下代码发布到您的线上正式版本的 App 中,原因如下:
*
* 本文件中的代码虽然能够正确计算出 UserSig,但仅适合快速调通 SDK 的基本功能,不适合线上产品,
* 这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解,尤其是 Web 端的代码被破解的难度几乎为零。
* 一旦您的密钥泄露,攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量。
*
* 正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上,然后由 App 按需向您的服务器获取实时算出的 UserSig。
* 由于破解服务器的成本要高于破解客户端 App,所以服务器计算的方案能够更好地保护您的加密密钥。
*
* Reference:https://cloud.tencent.com/document/product/269/32688#Server
*/
public class GenerateTestUserSig {
/**
* 腾讯云 SDKAppId,需要替换为您自己账号下的 SDKAppId。
* <p>
* 进入腾讯云云通信[控制台](https://console.cloud.tencent.com/avc ) 创建应用,即可看到 SDKAppId,
* 它是腾讯云用于区分客户的唯一标识。
*/
// public static final int SDKAPPID = 1400605334;
public static final int SDKAPPID = 1400655771;
/**
* 签名过期时间,建议不要设置的过短
* <p>
* 时间单位:秒
* 默认时间:7 x 24 x 60 x 60 = 604800 = 7 天
*/
private static final int EXPIRETIME = 604800;
/**
* 向后台请求推拉流地址
* <p>
* key:url_push 获取RTMP推流地址
* key:url_play_flv 获取FLV播放地址
*/
public static final String URL_FETCH_PUSH_URL = "PLACEHOLDER";
/**
* 计算签名用的加密密钥,获取步骤如下:
* <p>
* step1. 进入腾讯云云通信[控制台](https://console.cloud.tencent.com/avc ) ,如果还没有应用就创建一个,
* step2. 单击“应用配置”进入基础配置页面,并进一步找到“帐号体系集成”部分。
* step3. 点击“查看密钥”按钮,就可以看到计算 UserSig 使用的加密的密钥了,请将其拷贝并复制到如下的变量中
* <p>
* 注意:该方案仅适用于调试Demo,正式上线前请将 UserSig 计算代码和密钥迁移到您的后台服务器上,以避免加密密钥泄露导致的流量盗用。
* 文档:https://cloud.tencent.com/document/product/269/32688#Server
*/
// private static final String SECRETKEY = "ab68aea016757cab8bde7bc2b72c60ee97a359b4f61423e4ea7e864bb5362955";
private static final String SECRETKEY = "df460a2cdc1b2831370acd08ab939e9793c09fd4a615b1c503abfcdc866a9ef0";
/**
* 计算 UserSig 签名
* <p>
* 函数内部使用 HMAC-SHA256 非对称加密算法,对 SDKAPPID、userId 和 EXPIRETIME 进行加密。
*
* @note: 请不要将如下代码发布到您的线上正式版本的 App 中,原因如下:
* <p>
* 本文件中的代码虽然能够正确计算出 UserSig,但仅适合快速调通 SDK 的基本功能,不适合线上产品,
* 这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解,尤其是 Web 端的代码被破解的难度几乎为零。
* 一旦您的密钥泄露,攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量。
* <p>
* 正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上,然后由 App 按需向您的服务器获取实时算出的 UserSig。
* 由于破解服务器的成本要高于破解客户端 App,所以服务器计算的方案能够更好地保护您的加密密钥。
* <p>
* 文档:https://cloud.tencent.com/document/product/269/32688#Server
*/
public static String genTestUserSig(String userId) {
return GenTLSSignature(SDKAPPID, userId, EXPIRETIME, null, SECRETKEY);
}
/**
* 生成 tls 票据
*
* @param sdkappid 应用的 appid
* @param userId 用户 id
* @param expire 有效期,单位是秒
* @param userbuf 默认填写null
* @param priKeyContent 生成 tls 票据使用的私钥内容
* @return 如果出错,会返回为空,或者有异常打印,成功返回有效的票据
*/
private static String GenTLSSignature(long sdkappid, String userId, long expire, byte[] userbuf, String priKeyContent) {
if (TextUtils.isEmpty(priKeyContent)) {
return "";
}
long currTime = System.currentTimeMillis() / 1000;
JSONObject sigDoc = new JSONObject();
try {
sigDoc.put("TLS.ver", "2.0");
sigDoc.put("TLS.identifier", userId);
sigDoc.put("TLS.sdkappid", sdkappid);
sigDoc.put("TLS.expire", expire);
sigDoc.put("TLS.time", currTime);
} catch (JSONException e) {
e.printStackTrace();
}
String base64UserBuf = null;
if (null != userbuf) {
base64UserBuf = Base64.encodeToString(userbuf, Base64.NO_WRAP);
try {
sigDoc.put("TLS.userbuf", base64UserBuf);
} catch (JSONException e) {
e.printStackTrace();
}
}
String sig = hmacsha256(sdkappid, userId, currTime, expire, priKeyContent, base64UserBuf);
if (sig.length() == 0) {
return "";
}
try {
sigDoc.put("TLS.sig", sig);
} catch (JSONException e) {
e.printStackTrace();
}
Deflater compressor = new Deflater();
compressor.setInput(sigDoc.toString().getBytes(Charset.forName("UTF-8")));
compressor.finish();
byte[] compressedBytes = new byte[2048];
int compressedBytesLength = compressor.deflate(compressedBytes);
compressor.end();
return new String(base64EncodeUrl(Arrays.copyOfRange(compressedBytes, 0, compressedBytesLength)));
}
private static String hmacsha256(long sdkappid, String userId, long currTime, long expire, String priKeyContent, String base64Userbuf) {
String contentToBeSigned = "TLS.identifier:" + userId + "\n"
+ "TLS.sdkappid:" + sdkappid + "\n"
+ "TLS.time:" + currTime + "\n"
+ "TLS.expire:" + expire + "\n";
if (null != base64Userbuf) {
contentToBeSigned += "TLS.userbuf:" + base64Userbuf + "\n";
}
try {
byte[] byteKey = priKeyContent.getBytes("UTF-8");
Mac hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec keySpec = new SecretKeySpec(byteKey, "HmacSHA256");
hmac.init(keySpec);
byte[] byteSig = hmac.doFinal(contentToBeSigned.getBytes("UTF-8"));
return new String(Base64.encode(byteSig, Base64.NO_WRAP));
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
private static byte[] base64EncodeUrl(byte[] input) {
byte[] base64 = new String(Base64.encode(input, Base64.NO_WRAP)).getBytes();
for (int i = 0; i < base64.length; ++i)
switch (base64[i]) {
case '+':
base64[i] = '*';
break;
case '/':
base64[i] = '-';
break;
case '=':
base64[i] = '_';
break;
default:
break;
}
return base64;
}
}
# faceshow
#### 介绍
星光聊天
#### 软件架构
软件架构说明
#### 安装教程
1. xxxx
2. xxxx
3. xxxx
#### 使用说明
1. xxxx
2. xxxx
3. xxxx
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
#### 码云特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目
5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
\ No newline at end of file
apply plugin: 'com.android.library'
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
minSdkVersion 19
targetSdkVersion 30
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
afterEvaluate {
generateReleaseBuildConfig.enabled = false
generateDebugBuildConfig.enabled = false
}
repositories {
flatDir {
dirs 'libs', '../cms/libs'
}
}
//android {
// compileSdkVersion COMPILE_SDK_VERSION as Integer
// buildToolsVersion BUILD_TOOLS_VERSION
//
// defaultConfig {
// minSdkVersion MIN_SDK_VERSION as Integer
// targetSdkVersion TARGET_SDK_VERSION as Integer
// versionName VERSION_NAME
// versionCode VERSION_CODE as Integer
// }
//
// buildTypes {
// release {
// minifyEnabled false
// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
// }
// }
//}
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
api "androidx.multidex:multidex:2.0.0"
api "androidx.recyclerview:recyclerview:1.0.0"
api "androidx.appcompat:appcompat:1.0.0"
api "com.google.android.material:material:1.0.0"
api "androidx.constraintlayout:constraintlayout:1.1.3"
api "com.squareup.okhttp3:logging-interceptor:$LOGGING_INTERCEPTOR_VERSION"
api "com.squareup.retrofit2:converter-gson:$CONVERTER_GSON_VERSION"
api "com.squareup.retrofit2:retrofit:$RETROFIT_VERSION"
api "com.squareup.okhttp3:okhttp:$OKHTTP_VERSION"
api "com.github.bumptech.glide:glide:$GLIDE_VERSION"
api "com.github.ctiao:DanmakuFlameMaster:$DANMAKU_FLAME_MASTER_VERSION"
api "de.hdodenhof:circleimageview:$CIRCLEIMAGEVIEW_VERSION"
api "com.google.code.gson:gson:$GSON_VERSION"
api "com.blankj:utilcode:$UTIL_CODE_VERSION"
api project(':Debug')
implementation project(':cms')
implementation 'com.github.li-xiaojun:XPopup:2.0.0'
api "com.tencent.liteav:LiteAVSDK_TRTC:latest.release"
api "com.tencent.imsdk:imsdk-plus:latest.release"
}
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tencent.liteav.trtcvoiceroom">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application>
<activity
android:name=".ui.room.VoiceRoomAnchorActivity"
android:configChanges="orientation|keyboardHidden"
android:launchMode="singleTop"
android:theme="@style/TRTCVoiceRoomChatTheme"
android:screenOrientation="portrait" />
<activity
android:name=".ui.room.VoiceRoomAudienceActivity"
android:configChanges="orientation|keyboardHidden"
android:launchMode="singleTop"
android:theme="@style/TRTCVoiceRoomChatTheme"
android:screenOrientation="portrait" />
</application>
</manifest>
\ No newline at end of file
package com.tencent.liteav.basic;
public interface AvatarConstant {
String USER_AVATAR_ARRAY [] = {
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar1.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar2.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar3.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar4.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar5.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar6.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar7.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar8.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar9.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar10.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar11.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar12.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar13.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar14.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar15.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar16.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar17.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar18.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar19.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar20.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar21.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar22.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar23.png",
"https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar24.png",
};
}
package com.tencent.liteav.basic;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import androidx.annotation.DrawableRes;
import androidx.annotation.RawRes;
import android.text.TextUtils;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import com.bumptech.glide.request.RequestOptions;
import java.security.MessageDigest;
import java.util.concurrent.ExecutionException;
public class ImageLoader {
private static int radius = 15; //TRTC默认图片圆角为15dp
public static void clear(Context context, ImageView imageView) {
Glide.with(context).clear(imageView);
}
public static void loadImage(Context context, ImageView imageView, String url) {
loadImage(context, imageView, url, 0, radius);
}
public static void loadImage(Context context, ImageView imageView, String url, @DrawableRes int errorResId) {
loadImage(context, imageView, url, errorResId, radius);
}
public static void loadImage(Context context, ImageView imageView, String url, @DrawableRes int errorResId, int radius) {
if (TextUtils.isEmpty(url)) {
if (imageView != null && errorResId != 0) {
imageView.setImageResource(errorResId);
}
return;
}
Glide.with(context).load(url).error(loadTransform(context, errorResId, radius)).into(imageView);
}
public static void loadImage(Context context, ImageView imageView, @RawRes @DrawableRes Integer resourceId) {
Glide.with(context).load(resourceId).into(imageView);
}
public static Bitmap getImage(Context context, String url, int width, int height) {
if (TextUtils.isEmpty(url)) {
return null;
}
try {
Bitmap bitmap = Glide.with(context)
.asBitmap()
.load(url)
.into(width, height)
.get();
return bitmap;
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
public static void loadImageThumbnail(Context context, ImageView imageView, String url, @DrawableRes int resourceId, int radius) {
Glide.with(context).load(url)
.apply(new RequestOptions().placeholder(resourceId).error(resourceId).centerCrop()
.transform(new GlideRoundTransform(imageView.getContext(), radius)))
.thumbnail(loadTransform(imageView.getContext(), resourceId, radius))
.into(imageView);
}
public static void loadGifImage(Context context, ImageView imageView, String url) {
loadGifImage(context, imageView, url, 0);
}
public static void loadGifImage(Context context, ImageView imageView, String url, @DrawableRes int errorResId) {
if (TextUtils.isEmpty(url)) {
if (imageView != null && errorResId != 0) {
imageView.setImageResource(errorResId);
}
return;
}
Glide.with(context).asGif().load(url).into(imageView);
}
public static void loadGifImage(Context context, ImageView imageView, @RawRes @DrawableRes int resourceId) {
Glide.with(context).asGif().load(resourceId).into(imageView);
}
private static RequestBuilder<Drawable> loadTransform(Context context, @DrawableRes int placeholderId, int radius) {
return Glide.with(context).load(placeholderId)
.apply(new RequestOptions().centerCrop().transform(new GlideRoundTransform(context, radius)));
}
public static class GlideRoundTransform extends BitmapTransformation {
private static float radius = 0f;
public GlideRoundTransform(Context context, int dp) {
radius = Resources.getSystem().getDisplayMetrics().density * dp;
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return roundCrop(pool, toTransform);
}
private static Bitmap roundCrop(BitmapPool pool, Bitmap source) {
if (source == null) return null;
Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
if (result == null) {
result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
canvas.drawRoundRect(rectF, radius, radius, paint);
return result;
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
}
}
}
package com.tencent.liteav.basic;
import java.io.Serializable;
public class UserModel implements Serializable {
public String phone;
public String userId;
public String userSig;
public String userName;
public String userAvatar;
public UserType userType = UserType.NONE;
public enum UserType {
NONE,
MEETING, // 视频会议
CALLING, // 语音/视频通话
CHAT_SALON, // 语音沙龙
VOICE_ROOM, // 语音聊天室
LIVE_ROOM // 视频互动直播
}
}
package com.tencent.liteav.basic;
import android.util.Log;
import com.blankj.utilcode.util.GsonUtils;
import com.blankj.utilcode.util.SPUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
public class UserModelManager {
private static final String TAG = "UserModelManager";
private static final String PER_DATA = "per_profile_manager";
private static final String PER_USER_MODEL = "per_user_model";
private static final String PER_USER_DATE = "per_user_publish_video_date";
private static UserModelManager sInstance;
private UserModel mUserModel;
private String mUserPubishVideoDate;
public static UserModelManager getInstance() {
if (sInstance == null) {
synchronized (UserModelManager.class) {
if (sInstance == null) {
sInstance = new UserModelManager();
}
}
}
return sInstance;
}
public synchronized UserModel getUserModel() {
if (mUserModel == null) {
loadUserModel();
}
return mUserModel == null ? new UserModel() : mUserModel;
}
public synchronized void setUserModel(UserModel model) {
mUserModel = model;
try {
SPUtils.getInstance(PER_DATA).put(PER_USER_MODEL, GsonUtils.toJson(mUserModel));
} catch (Exception e) {
Log.d(TAG, "");
}
}
private void loadUserModel() {
try {
String json = SPUtils.getInstance(PER_DATA).getString(PER_USER_MODEL);
mUserModel = GsonUtils.fromJson(json, UserModel.class);
} catch (Exception e) {
}
}
private String getUserPublishVideoDate() {
if (mUserPubishVideoDate == null) {
mUserPubishVideoDate = SPUtils.getInstance(PER_DATA).getString(PER_USER_DATE, "");
}
return mUserPubishVideoDate;
}
private void setUserPublishVideoDate(String date) {
mUserPubishVideoDate = date;
try {
SPUtils.getInstance(PER_DATA).put(PER_USER_DATE, mUserPubishVideoDate);
} catch (Exception e) {
}
}
// 首次TRTC打开摄像头提示"Demo特别配置了无限期云端存储"
public boolean needShowSecurityTips() {
String profileDate = getUserPublishVideoDate();
Date date = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("dd");
String day = formatter.format(date);
if (!day.equals(profileDate)) {
setUserPublishVideoDate(day);
return true;
}
return false;
}
}
\ No newline at end of file
package com.tencent.liteav.trtcvoiceroom.model;
import java.util.List;
public class TRTCVoiceRoomCallback {
/**
* 通用回调
*/
public interface ActionCallback {
void onCallback(int code, String msg);
}
/**
* 获取房间信息回调
*/
public interface RoomInfoCallback {
void onCallback(int code, String msg, List<TRTCVoiceRoomDef.RoomInfo> list);
}
/**
* 获取成员信息回调
*/
public interface UserListCallback {
void onCallback(int code, String msg, List<TRTCVoiceRoomDef.UserInfo> list);
}
}
package com.tencent.liteav.trtcvoiceroom.model;
import java.util.List;
public class TRTCVoiceRoomDef {
//当前app module定义的版本号
public static final String APP_VERSION = "9.5.0.1479";
public static final int ERR_SVR_GROUP_ATTRIBUTE_WRITE_CONFLICT = 10056; //群属性写冲突,请先拉取最新的群属性后再尝试写操作,IMSDK5.6及其以上版本支持,麦位信息已经发生变化,需要重新拉取
public static final int ERR_CALL_METHOD_LIMIT = 10001; //接口调用限频
public static class SeatInfo {
public static final transient int STATUS_UNUSED = 0;
public static final transient int STATUS_USED = 1;
public static final transient int STATUS_CLOSE = 2;
/// 【字段含义】座位状态 0(unused)/1(used)/2(close)
public int status;
/// 【字段含义】座位是否禁言
public boolean mute;
/// 【字段含义】座位状态为1,存储userInfo
public String userId;
@Override
public String toString() {
return "TXSeatInfo{" +
"status=" + status +
", mute=" + mute +
", userId=" + userId +
'}';
}
}
public static class RoomParam {
/// 【字段含义】房间名称
public String roomName;
/// 【字段含义】房间封面图
public String coverUrl;
public int bg;
/// 【字段含义】是否需要房主确认上麦
public boolean needRequest;
/// 【字段含义】座位数
public int seatCount;
/// 【字段含义】初始化的座位表,可以为null
public List<SeatInfo> seatInfoList;
@Override
public String toString() {
return "RoomParam{" +
"roomName='" + roomName + '\'' +
", coverUrl='" + coverUrl + '\'' +
", needRequest=" + needRequest +
", seatInfoList=" + seatInfoList +
'}';
}
}
public static class UserInfo {
/// 【字段含义】用户唯一标识
public String userId;
/// 【字段含义】用户昵称
public String userName;
/// 【字段含义】用户头像
public String userAvatar;
@Override
public String toString() {
return "UserInfo{" +
"userId='" + userId + '\'' +
", userName='" + userName + '\'' +
", userAvatar='" + userAvatar + '\'' +
'}';
}
}
public static class RoomInfo {
/// 【字段含义】房间唯一标识
public int roomId;
/// 【字段含义】房间名称
public String roomName;
//房间话题
public String roomTopic;
/// 【字段含义】房间封面图
public String coverUrl;
public int bg;
/// 【字段含义】房主id
public String ownerId;
/// 【字段含义】房主昵称
public String ownerName;
/// 【字段含义】房间人数
public int memberCount;
/// 【字段含义】是否需要房主确认上麦
public boolean needRequest;
@Override
public String toString() {
return "TXRoomInfo{" +
"roomId=" + roomId +
", roomName='" + roomName + '\'' +
", coverUrl='" + coverUrl + '\'' +
", ownerId='" + ownerId + '\'' +
", ownerName='" + ownerName + '\'' +
", memberCount=" + memberCount +
", needRequest=" + needRequest +
'}';
}
}
}
package com.tencent.liteav.trtcvoiceroom.model;
import com.tencent.liteav.trtcvoiceroom.model.TRTCVoiceRoomDef.SeatInfo;
import com.tencent.trtc.TRTCCloudDef;
import java.util.List;
public interface TRTCVoiceRoomDelegate {
/**
* 组件出错信息,请务必监听并处理
*/
void onError(int code, String message);
/**
* 组件告警信息
*/
void onWarning(int code, String message);
/**
* 组件log信息
*/
void onDebugLog(String message);
/**
* 房间被销毁,当主播调用destroyRoom后,观众会收到该回调
*/
void onRoomDestroy(String roomId);
/**
* 房间信息改变的通知
*/
void onRoomInfoChange(TRTCVoiceRoomDef.RoomInfo roomInfo);
/**
* 全量的麦位列表变化,包含了整个麦位表
* @param seatInfoList 全量的麦位列表
*/
void onSeatListChange(List<SeatInfo> seatInfoList);
/**
* 有成员上麦(主动上麦/主播抱人上麦)
* @param index 上麦的麦位
* @param user 用户详细信息
*/
void onAnchorEnterSeat(int index, TRTCVoiceRoomDef.UserInfo user);
/**
* 有成员下麦(主动下麦/主播踢人下麦)
* @param index 下麦的麦位
* @param user 用户详细信息
*/
void onAnchorLeaveSeat(int index, TRTCVoiceRoomDef.UserInfo user);
/**
* 主播禁麦
* @param index 操作的麦位
* @param isMute 是否静音
*/
void onSeatMute(int index, boolean isMute);
/**
* 用户麦克风是否静音
* @param userId 用户id
* @param mute 是否静音
*/
void onUserMicrophoneMute(String userId, boolean mute);
/**
* 主播封麦
* @param index 操作的麦位
* @param isClose 是否封禁麦位
*/
void onSeatClose(int index, boolean isClose);
/**
* 观众进入房间
*
* @param userInfo 观众的详细信息
*/
void onAudienceEnter(TRTCVoiceRoomDef.UserInfo userInfo);
/**
* 观众离开房间
*
* @param userInfo 观众的详细信息
*/
void onAudienceExit(TRTCVoiceRoomDef.UserInfo userInfo);
/**
* 上麦成员的音量变化
*
* @param userVolumes 用户列表
* @param totalVolume 音量大小 0-100
*/
void onUserVolumeUpdate(List<TRTCCloudDef.TRTCVolumeInfo> userVolumes, int totalVolume);
/**
* 收到文本消息。
*
* @param message 文本消息。
* @param userInfo 发送者用户信息。
*/
void onRecvRoomTextMsg(String message, TRTCVoiceRoomDef.UserInfo userInfo);
/**
* 收到自定义消息。
*
* @param cmd 命令字,由开发者自定义,主要用于区分不同消息类型。
* @param message 文本消息。
* @param userInfo 发送者用户信息。
*/
void onRecvRoomCustomMsg(String cmd, String message, TRTCVoiceRoomDef.UserInfo userInfo);
/**
* 收到新的邀请请求
*
* @param id 邀请id
* @param inviter 邀请人userId
* @param cmd 业务指定的命令字
* @param content 业务指定的内容
*/
void onReceiveNewInvitation(String id, String inviter, String cmd, String content);
/**
* 被邀请者接受邀请
*
* @param id 邀请id
* @param invitee 被邀请人userId
*/
void onInviteeAccepted(String id, String invitee);
/**
* 被邀请者拒绝邀请
*
* @param id 邀请id
* @param invitee 被邀请人userId
*/
void onInviteeRejected(String id, String invitee);
/**
* 邀请人取消邀请
*
* @param id 邀请id
* @param inviter 邀请人userId
*/
void onInvitationCancelled(String id, String inviter);
}
package com.tencent.liteav.trtcvoiceroom.model;
import android.util.Log;
import com.tencent.imsdk.v2.V2TIMGroupInfoResult;
import com.tencent.imsdk.v2.V2TIMManager;
import com.tencent.imsdk.v2.V2TIMValueCallback;
import java.util.ArrayList;
import java.util.List;
public class VoiceRoomManager {
private static final String TAG = "VoiceRoomManager";
private static VoiceRoomManager sInstance;
private RoomCallback mRoomCallback;
public static VoiceRoomManager getInstance() {
if (sInstance == null) {
synchronized (VoiceRoomManager.class) {
if (sInstance == null) {
sInstance = new VoiceRoomManager();
}
}
}
return sInstance;
}
public void addCallback(RoomCallback callback) {
mRoomCallback = callback;
}
public void removeCallback() {
mRoomCallback = null;
}
public void createRoom(int roomId, ActionCallback callback) {
if (mRoomCallback != null) {
mRoomCallback.onRoomCreate(roomId, callback);
}
}
public void destroyRoom(int roomId, ActionCallback callback) {
if (mRoomCallback != null) {
mRoomCallback.onRoomDestroy(roomId, callback);
}
}
public interface RoomCallback {
void onRoomCreate(int roomId, ActionCallback callback);
void onRoomDestroy(int roomId, ActionCallback callback);
}
public interface ActionCallback {
void onSuccess();
void onError(int errorCode, String message);
}
public void getGroupInfo(final String roomId, final GetGroupInfoCallback callback) {
List<String> roomIdList = new ArrayList<>();
roomIdList.add(roomId);
Log.i(TAG, "get room id list " + roomIdList);
V2TIMManager.getGroupManager().getGroupsInfo(roomIdList, new V2TIMValueCallback<List<V2TIMGroupInfoResult>>() {
@Override
public void onError(int i, String s) {
Log.e(TAG, "get group info list fail, code:" + i+ " msg: " + s);
callback.onFailed(-1, s);
}
@Override
public void onSuccess(List<V2TIMGroupInfoResult> resultList) {
if (resultList != null && !resultList.isEmpty()) {
V2TIMGroupInfoResult result = resultList.get(0);
callback.onSuccess(result);
} else {
callback.onFailed(-1, "get groupInfo List is null");
}
}
});
}
// 通过房间号获取房间信息的回调
public interface GetGroupInfoCallback {
void onSuccess(V2TIMGroupInfoResult result);
void onFailed(int code, String msg);
}
}
package com.tencent.liteav.trtcvoiceroom.model.impl.base;
import com.tencent.liteav.basic.log.TXCLog;
public class TRTCLogger {
public static void e(String tag, String message) {
TXCLog.e(tag, message);
callback("e", tag, message);
}
public static void w(String tag, String message) {
TXCLog.w(tag, message);
callback("w", tag, message);
}
public static void i(String tag, String message) {
TXCLog.i(tag, message);
callback("i", tag, message);
}
public static void d(String tag, String message) {
TXCLog.d(tag, message);
callback("d", tag, message);
}
private static void callback(String level, String tag, String message) {
}
}
package com.tencent.liteav.trtcvoiceroom.model.impl.base;
public interface TXCallback {
void onCallback(int code, String msg);
}
package com.tencent.liteav.trtcvoiceroom.model.impl.base;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
public class TXRoomInfo implements Serializable {
/// 这个变量仅仅在本地使用
public transient String roomId;
/// 这个变量仅仅在本地使用
public transient int memberCount;
@SerializedName("ownerId")
@Expose
public String ownerId;
@SerializedName("ownerName")
@Expose
public String ownerName;
@SerializedName("roomName")
@Expose
public String roomName;
@SerializedName("roomTopic")
@Expose
public String roomTopic;
@SerializedName("cover")
@Expose
public String cover;
@SerializedName("seatSize")
@Expose
public Integer seatSize;
@SerializedName("needRequest")
@Expose
public Integer needRequest;
@Override
public String toString() {
return "TXRoomInfo{" +
"roomId='" + roomId + '\'' +
", memberCount=" + memberCount +
", ownerId='" + ownerId + '\'' +
", ownerName='" + ownerName + '\'' +
", roomName='" + roomName + '\'' +
", cover='" + cover + '\'' +
", seatSize=" + seatSize +
", needRequest=" + needRequest +
'}';
}
}
package com.tencent.liteav.trtcvoiceroom.model.impl.base;
import java.util.List;
public interface TXRoomInfoListCallback {
void onCallback(int code, String msg, List<TXRoomInfo> list);
}
package com.tencent.liteav.trtcvoiceroom.model.impl.base;
import java.io.Serializable;
public class TXSeatInfo implements Serializable {
public static final transient int STATUS_UNUSED = 0;
public static final transient int STATUS_USED = 1;
public static final transient int STATUS_CLOSE = 2;
/// 【字段含义】座位状态 0(unused)/1(used)/2(close)
public int status;
/// 【字段含义】座位是否禁言
public boolean mute;
/// 【字段含义】座位状态为1,存储user
public String user;
@Override
public String toString() {
return "TXSeatInfo{" +
"status=" + status +
", mute=" + mute +
", userInfo=" + user +
'}';
}
}
\ No newline at end of file
package com.tencent.liteav.trtcvoiceroom.model.impl.base;
import java.io.Serializable;
public class TXUserInfo implements Serializable {
public String userId;
public String userName;
public String avatarURL;
@Override
public String toString() {
return "TXUserInfo{" +
"userId='" + userId + '\'' +
", userName='" + userName + '\'' +
", avatarURL='" + avatarURL + '\'' +
'}';
}
}
package com.tencent.liteav.trtcvoiceroom.model.impl.base;
import java.util.List;
public interface TXUserListCallback {
void onCallback(int code, String msg, List<TXUserInfo> list);
}
package com.tencent.liteav.trtcvoiceroom.model.impl.room;
import com.tencent.liteav.trtcvoiceroom.model.impl.base.TXRoomInfo;
import com.tencent.liteav.trtcvoiceroom.model.impl.base.TXSeatInfo;
import com.tencent.liteav.trtcvoiceroom.model.impl.base.TXUserInfo;
import java.util.List;
public interface ITXRoomServiceDelegate {
void onRoomDestroy(String roomId);
void onRoomRecvRoomTextMsg(String roomId, String message, TXUserInfo userInfo);
void onRoomRecvRoomCustomMsg(String roomId, String cmd, String message, TXUserInfo userInfo);
void onRoomInfoChange(TXRoomInfo TXRoomInfo);
void onSeatInfoListChange(List<TXSeatInfo> TXSeatInfoList);
void onRoomAudienceEnter(TXUserInfo userInfo);
void onRoomAudienceLeave(TXUserInfo userInfo);
void onSeatTake(int index, TXUserInfo userInfo);
void onSeatClose(int index, boolean isClose);
void onSeatLeave(int index, TXUserInfo userInfo);
void onSeatMute(int index, boolean mute);
void onReceiveNewInvitation(String id, String inviter, String cmd, String content);
void onInviteeAccepted(String id, String invitee);
void onInviteeRejected(String id, String invitee);
void onInvitationCancelled(String id, String inviter);
}
package com.tencent.liteav.trtcvoiceroom.model.impl.room.impl;
public class SignallingData {
private int version;
private String businessID;
private String platform;
private String extInfo;
private DataInfo data;
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
public String getBusinessID() {
return businessID;
}
public void setBusinessID(String businessID) {
this.businessID = businessID;
}
public String getPlatform() {
return platform;
}
public void setPlatform(String platform) {
this.platform = platform;
}
public String getExtInfo() {
return extInfo;
}
public void setExtInfo(String extInfo) {
this.extInfo = extInfo;
}
public DataInfo getData() {
return data;
}
public void setData(DataInfo data) {
this.data = data;
}
public static class DataInfo {
private int room_id;
private String cmd;
private String seat_number;
public int getRoomID() {
return room_id;
}
public void setRoomID(int roomId) {
this.room_id = roomId;
}
public String getCmd() {
return cmd;
}
public void setCmd(String cmd) {
this.cmd = cmd;
}
public String getSeatNumber() {
return seat_number;
}
public void setSeatNumber(String seatNumber) {
this.seat_number = seatNumber;
}
}
}
package com.tencent.liteav.trtcvoiceroom.model.impl.trtc;
import com.tencent.trtc.TRTCCloudDef;
import java.util.ArrayList;
public interface VoiceRoomTRTCServiceDelegate {
void onTRTCAnchorEnter(String userId);
void onTRTCAnchorExit(String userId);
void onTRTCAudioAvailable(String userId, boolean available);
void onError(int errorCode, String errorMsg);
void onNetworkQuality(TRTCCloudDef.TRTCQuality trtcQuality, ArrayList<TRTCCloudDef.TRTCQuality> arrayList);
void onUserVoiceVolume(ArrayList<TRTCCloudDef.TRTCVolumeInfo> userVolumes, int totalVolume);
}
package com.tencent.liteav.trtcvoiceroom.ui.base;
import android.content.Context;
public class EarMonitorInstance {
private static EarMonitorInstance sInstance;
private boolean mIsEarMonitorOpen;
public static synchronized EarMonitorInstance getInstance() {
if (sInstance == null) {
sInstance = new EarMonitorInstance();
}
return sInstance;
}
public boolean ismEarMonitorOpen() {
return mIsEarMonitorOpen;
}
public void updateEarMonitorState(boolean isOpen) {
mIsEarMonitorOpen = isOpen;
}
}
package com.tencent.liteav.trtcvoiceroom.ui.base;
import com.tencent.liteav.trtcvoiceroom.model.TRTCVoiceRoomDef;
public class MemberEntity extends TRTCVoiceRoomDef.UserInfo {
public static final int TYPE_IDEL = 0;
public static final int TYPE_IN_SEAT = 1;
public static final int TYPE_WAIT_AGREE = 2;
public int type;
}
package com.tencent.liteav.trtcvoiceroom.ui.base;
public class VoiceRoomSeatEntity {
public int index;
public String userId;
public String userName;
public String userAvatar;
public boolean isUsed;
public boolean isClose;
public boolean isSeatMute;
public boolean isUserMute = true;
public boolean isTalk;
}
package com.tencent.liteav.trtcvoiceroom.ui.room;
/**
* Module: TCConstants
* <p>
* Function: 定义常量的类
*/
public class TCConstants {
//直播端右下角listview显示type
public static final String CMD_REQUEST_TAKE_SEAT = "takeSeat";
public static final String CMD_PICK_UP_SEAT = "pickSeat";
}
package com.tencent.liteav.trtcvoiceroom.ui.room;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.SwitchCompat;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import com.blankj.utilcode.util.ToastUtils;
import com.tencent.liteav.basic.UserModelManager;
import com.tencent.liteav.trtcvoiceroom.R;
/**
* 创建语聊房页面
*
* @author guanyifeng
*/
public class VoiceRoomCreateDialog extends BottomSheetDialog {
private EditText mRoomNameEt;
private TextView mEnterTv;
private SwitchCompat mSwitchBtn;
private String mUserName;
private String mUserId;
private String mCoverUrl;
private int mAudioQuality;
private boolean mNeedRequest;
private int MAX_LEN = 30;
public void showVoiceRoomCreateDialog(String userId, String userName, String coverUrl, int audioQuality,
boolean needRequest) {
mUserId = userId;
mUserName = userName;
mCoverUrl = coverUrl;
mAudioQuality = audioQuality;
mNeedRequest = needRequest;
show();
}
private TextWatcher mEditTextWatcher = 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) {
if (!TextUtils.isEmpty(mRoomNameEt.getText().toString())) {
mEnterTv.setEnabled(true);
} else {
mEnterTv.setEnabled(false);
}
}
@Override
public void afterTextChanged(Editable s) {
}
};
public VoiceRoomCreateDialog(@NonNull Context context) {
super(context, R.style.TRTCVoiceRoomDialogTheme);
setContentView(R.layout.trtcvoiceroom_dialog_create_voice_room);
initView();
initData();
}
private void initData() {
mUserName = UserModelManager.getInstance().getUserModel().userName;
mUserId = UserModelManager.getInstance().getUserModel().userId;
mRoomNameEt.addTextChangedListener(mEditTextWatcher);
mEnterTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
createRoom();
}
});
String showName = TextUtils.isEmpty(mUserName) ? mUserId : mUserName;
mRoomNameEt.setText(getContext().getString(R.string.trtcvoiceroom_create_theme, showName));
}
private void createRoom() {
String roomName = mRoomNameEt.getText().toString();
if (TextUtils.isEmpty(roomName)) {
return;
}
if (roomName.getBytes().length > MAX_LEN) {
ToastUtils.showLong(getContext().getText(R.string.trtcvoiceroom_warning_room_name_too_long));
return;
}
//VoiceRoomAnchorActivity.createRoom(getContext(), roomName, mUserId, mUserName, mCoverUrl, mAudioQuality, mSwitchBtn.isChecked());
dismiss();
}
private void initView() {
mRoomNameEt = (EditText) findViewById(R.id.et_room_name);
mEnterTv = (TextView) findViewById(R.id.tv_enter);
mSwitchBtn = (SwitchCompat) findViewById(R.id.switch_item);
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment