第一次提交

This commit is contained in:
xuhuixiang
2025-08-08 15:05:12 +08:00
parent 3e7f1d9089
commit ad722304bf
795 changed files with 19580 additions and 9048 deletions

6
call_sw/.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
/build
agora-*
libagora*
libvideo*
libudrm3.so
/release/

73
call_sw/build.gradle Normal file
View File

@@ -0,0 +1,73 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdk 33
namespace = "io.agora.onetoone"
defaultConfig {
ndk.abiFilters 'arm64-v8a', 'armeabi-v7a'
minSdk 24
targetSdk 33
versionCode 1
versionName "2.1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField("String", "AG_APP_ID", "\"${project.getProperty("AG_APP_ID")}\"")
buildConfigField("String", "AG_APP_CERTIFICATE", "\"${project.getProperty("AG_APP_CERTIFICATE")}\"")
buildConfigField "String", "IM_APP_KEY", "\"${IM_APP_KEY}\""
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
}
packagingOptions {
pickFirst 'lib/*/libaosl.so'
}
}
dependencies {
api 'androidx.core:core-ktx:1.7.0'
api 'androidx.appcompat:appcompat:1.4.1'
api 'com.google.android.material:material:1.5.0'
api 'androidx.constraintlayout:constraintlayout:2.1.3'
api 'androidx.navigation:navigation-fragment-ktx:2.4.1'
api 'androidx.navigation:navigation-ui-ktx:2.4.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// api 'com.squareup.okhttp3:okhttp:3.12.0'
api 'com.squareup.okhttp3:okhttp:4.9.3'
api 'com.github.GrenderG:Toasty:1.5.0'
api 'com.orhanobut:logger:2.2.0'
// rtc
api 'io.agora:authentication:1.6.1'
api 'commons-codec:commons-codec:1.15'
// callapi
api project(":lib_callapi")
api project(":chatkit-ui")
}

21
call_sw/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# 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

View File

@@ -0,0 +1,24 @@
package io.agora.onetoone
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("io.agora.callapi", appContext.packageName)
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<application>
</application>
</manifest>

View File

@@ -0,0 +1,104 @@
package io.agora.onetoone.http
import android.os.Handler
import android.os.Looper
import android.util.Log
import io.agora.onetoone.BuildConfig
import okhttp3.*
import org.json.JSONException
import org.json.JSONObject
import org.json.JSONArray
import java.io.IOException
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
object HttpManager {
private val TAG = "HttpManager_LOG"
private const val mBaseUrl = "https://toolbox.bj2.agoralab.co/v1"
private val mClient = OkHttpClient()
// fun sendPostRequest() {
//
// val requestBody = RequestBody.create(MediaType.parse("application/json"), "{\"name\":\"John\", \"age\":30}")
//
// val request = Request.Builder()
// .url(mBaseUrl)
// .post(requestBody)
// .build()
//
// mClient.newCall(request).enqueue(object : Callback {
// override fun onFailure(call: Call, e: IOException) {
// e.printStackTrace()
// }
//
// override fun onResponse(call: Call, response: Response) {
// val responseBody = response.body()?.string()
// println(responseBody)
// }
// })
// }
// 1: RTC Token ; 2: RTM Token
fun token007(channelName: String, uid: String, onCompletion: ((String?) -> Unit)?) {
val postBody = JSONObject()
val types = arrayOf(1,2)
val jsonArray = JSONArray(types)
try {
postBody.put("appId", BuildConfig.AG_APP_ID)
postBody.put("appCertificate", BuildConfig.AG_APP_CERTIFICATE)
postBody.put("channelName", channelName)
postBody.put("expire", 1500) // expire seconds
postBody.put("src", "Android")
postBody.put("ts", System.currentTimeMillis())
postBody.put("types", jsonArray)
postBody.put("uid", uid)
} catch (e: JSONException) {
onCompletion?.invoke(null)
}
val requestBody = RequestBody.create(null, postBody.toString())
val request = Request.Builder()
.url("$mBaseUrl/token/generate")
.addHeader("Content-Type", "application/json")
.post(requestBody)
.build()
mClient.newCall(request).enqueue(object : Callback {
override fun onResponse(call: Call, response: Response) {
val json = response.body?.string() ?: ""
val map = parseJson(json)
val data = map["data"] as? Map<String, Any>
if (data != null) {
val token = data["token"] as? String
runOnUiThread {
onCompletion?.invoke(token)
}
} else {
runOnUiThread {
onCompletion?.invoke(null)
}
}
Log.d(TAG, "HTTP response: $json")
}
override fun onFailure(call: Call, e: IOException) {
Log.d(TAG, "HTTP error")
runOnUiThread {
onCompletion?.invoke(null)
}
}
})
}
private fun runOnUiThread(runnable: Runnable) {
if (Thread.currentThread() === Looper.getMainLooper().thread) {
runnable.run()
} else {
Handler(Looper.getMainLooper()).post(runnable)
}
}
fun parseJson(json: String): Map<String, Any> {
val gson = Gson()
val type = object : TypeToken<Map<String, Any>>() {}.type
return gson.fromJson(json, type)
}
}

View File

@@ -0,0 +1,28 @@
package io.agora.onetoone.model
import java.io.Serializable
/** Information carried when joining a room
* 加入房间时携带的信息
*/
data class EnterRoomInfoModel (
var isRtm: Boolean = true,
var isBrodCaster: Boolean = true,
var currentUid: String = "",
var showRoomId: String = "",
var showUserId: String = "",
var rtcToken: String = "",
var rtmToken: String = "",
var showRoomToken: String = "",
var dimensionsWidth: String = "",
var dimensionsHeight: String = "",
var frameRate: String = "",
var bitrate: String = "0",
var autoAccept: Boolean = true,
var autoJoinRTC: Boolean = false,
var firstFrameWaittingDisabled: Boolean = false
) : Serializable

View File

@@ -0,0 +1,191 @@
package io.agora.onetoone.signalClient
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.widget.Toast
import com.hyphenate.EMCallBack
import com.hyphenate.EMConnectionListener
import com.hyphenate.EMMessageListener
import com.hyphenate.chat.EMClient
import com.hyphenate.chat.EMMessage
import com.hyphenate.chat.EMOptions
import com.hyphenate.chat.EMTextMessageBody
import com.hyphenate.exceptions.HyphenateException
import es.dmoral.toasty.Toasty
import io.agora.onetoone.AGError
import io.agora.onetoone.R
import io.agora.rtm.PublishOptions
import io.agora.rtm.RtmConstants
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
fun createEasemobSignalClient(context: Context, appKey: String, userId: Int) = CallEasemobSignalClient(context, appKey, userId)
class CallEasemobSignalClient(
private val context: Context,
private val appKey: String,
private val userId: Int = 0
): ISignalClient, EMMessageListener, EMConnectionListener, CallBaseSignalClient() {
companion object {
private const val TAG = "CALL_HY_MSG_MANAGER"
}
init {
val options = EMOptions()
options.appKey = appKey
// Other EMOptions configurations
// 其他 EMOptions 配置
EMClient.getInstance().init(context, options)
// Register message listener
// 注册消息监听
EMClient.getInstance().chatManager().addMessageListener(this)
EMClient.getInstance().addConnectionListener(this)
}
var isConnected: Boolean = false
private val mHandler = Handler(Looper.getMainLooper())
fun login(completion: ((success: Boolean) -> Unit)?) {
Thread {
// Registration
// 注册
try {
// Synchronous method, will block current thread
// 同步方法,会阻塞当前线程
EMClient.getInstance().createAccount(userId.toString(), userId.toString())
} catch (e: HyphenateException) {
// Failed
// 失败
Log.e(TAG, "createAccount failed: ${e.message}, code: ${e.errorCode}")
}
// Login
// 登陆
EMClient.getInstance().login(userId.toString(), userId.toString(), object : EMCallBack {
// Login success callback
// 登录成功回调
override fun onSuccess() {
Log.d(TAG, "login success")
isConnected = true
EMClient.getInstance().chatManager().loadAllConversations()
EMClient.getInstance().groupManager().loadAllGroups()
completion?.invoke(true)
}
// Login failure callback with error information
// 登录失败回调,包含错误信息
override fun onError(code: Int, error: String) {
Log.e(TAG, "login failed, code:$code, msg:$error")
completion?.invoke(false)
}
override fun onProgress(i: Int, s: String) {}
})
}.start()
}
fun clean() {
isConnected = false
listeners.clear()
EMClient.getInstance().removeConnectionListener(this)
EMClient.getInstance().logout(false)
}
override fun sendMessage(
userId: String,
message: String,
completion: ((AGError?) -> Unit)?
) {
if (userId.isEmpty() || userId == "0") {
val errorStr = "sendMessage fail, invalid userId[$userId]"
completion?.invoke(AGError(errorStr, -1))
return
}
innerSendMessage(userId, message, completion)
}
private fun innerSendMessage(
userId: String,
message: String,
completion: ((AGError?) -> Unit)?
) {
if (userId.isEmpty()) {
completion?.invoke(AGError("send message fail! roomId is empty", -1))
return
}
val options = PublishOptions()
options.setChannelType(RtmConstants.RtmChannelType.USER)
val startTime = System.currentTimeMillis()
// `content` is the text content to send, `toChatUsername` is the recipient's account
// `content` 为要发送的文本内容,`toChatUsername` 为对方的账号
val msg = EMMessage.createTextSendMessage(message, userId)
// Chat type: single chat is EMMessage.ChatType.Chat
// 会话类型:单聊为 EMMessage.ChatType.Chat
msg.chatType = EMMessage.ChatType.Chat
msg.deliverOnlineOnly(true)
// When sending a message, you can set an instance of `EMCallBack` to get the message sending status
// Can update message display status in this callback, such as message sending failure prompts, etc.
// 发送消息时可以设置 `EMCallBack` 的实例,获得消息发送的状态
// 可以在该回调中更新消息的显示状态。例如消息发送失败后的提示等等
msg.setMessageStatusCallback(object : EMCallBack {
override fun onSuccess() {
// Message sent successfully
// 发送消息成功
runOnUiThread { completion?.invoke(null) }
}
override fun onError(code: Int, error: String) {
// Message sending failed
// 发送消息失败
runOnUiThread { completion?.invoke(AGError(error, code)) }
}
override fun onProgress(progress: Int, status: String) {}
})
// Send message
// 发送消息
EMClient.getInstance().chatManager().sendMessage(msg)
}
// ---------------- EMMessageListener ----------------
override fun onMessageReceived(messages: MutableList<EMMessage>?) {
runOnUiThread {
messages?.forEach {
val body = it.body as EMTextMessageBody
listeners.forEach {
it.onMessageReceive(body.message)
}
}
}
}
// ---------------- EMConnectionListener ----------------
override fun onConnected() {
runOnUiThread {
Toasty.normal(context, context.getString(R.string.toast_easemob_connected), Toast.LENGTH_SHORT).show()
}
isConnected = true
}
override fun onDisconnected(errorCode: Int) {
runOnUiThread {
Toasty.normal(context, context.getString(R.string.toast_easemob_disconnected), Toast.LENGTH_SHORT).show()
}
isConnected = false
}
// ---------------- inner private ----------------
private fun runOnUiThread(runnable: Runnable) {
if (Thread.currentThread() == Looper.getMainLooper().thread) {
runnable.run()
} else {
mHandler.post(runnable)
}
}
}

View File

@@ -0,0 +1,323 @@
package io.agora.onetoone.utils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.appcompat.widget.AppCompatImageView;
import io.agora.onetoone.R;
public class CircleImageView extends AppCompatImageView {
// paint when user press
private Paint pressPaint;
private int width;
private int height;
// default bitmap config
private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
private static final int COLORDRAWABLE_DIMENSION = 1;
// border color
private int borderColor;
// width of border
private int borderWidth;
// alpha when pressed
private int pressAlpha;
// color when pressed
private int pressColor;
// radius\
private int radius;
// rectangle or round, 1 is circle, 2 is rectangle
private int shapeType;
public CircleImageView(Context context) {
super(context);
init(context, null);
}
public CircleImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
//init the value
borderWidth = 0;
borderColor = 0xddffffff;
pressAlpha = 0x42;
pressColor = 0x42000000;
radius = 16;
shapeType = 0;
// get attribute of EaseImageView
if (attrs != null) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView);
borderColor = array.getColor(R.styleable.CircleImageView_ease_border_color, borderColor);
borderWidth = array.getDimensionPixelOffset(R.styleable.CircleImageView_ease_border_width, borderWidth);
pressAlpha = array.getInteger(R.styleable.CircleImageView_ease_press_alpha, pressAlpha);
pressColor = array.getColor(R.styleable.CircleImageView_ease_press_color, pressColor);
radius = array.getDimensionPixelOffset(R.styleable.CircleImageView_ease_radius, radius);
shapeType = array.getInteger(R.styleable.CircleImageView_es_shape_type, shapeType);
array.recycle();
}
// set paint when pressed
pressPaint = new Paint();
pressPaint.setAntiAlias(true);
pressPaint.setStyle(Paint.Style.FILL);
pressPaint.setColor(pressColor);
pressPaint.setAlpha(0);
pressPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
setDrawingCacheEnabled(true);
setWillNotDraw(false);
}
@Override
protected void onDraw(Canvas canvas) {
if (shapeType == 0) {
super.onDraw(canvas);
return;
}
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
// the width and height is in xml file
if (getWidth() == 0 || getHeight() == 0) {
return;
}
Bitmap bitmap = getBitmapFromDrawable(drawable);
drawDrawable(canvas, bitmap);
if (isClickable()) {
drawPress(canvas);
}
drawBorder(canvas);
}
/**
* draw Rounded Rectangle
*
* @param canvas
* @param bitmap
*/
@SuppressLint("WrongConstant")
private void drawDrawable(Canvas canvas, Bitmap bitmap) {
Paint paint = new Paint();
paint.setColor(0xffffffff);
paint.setAntiAlias(true); //smooths out the edges of what is being drawn
PorterDuffXfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
// set flags
int saveFlags = Canvas.ALL_SAVE_FLAG
;
canvas.saveLayer(0, 0, width, height, null, saveFlags);
if (shapeType == 1) {
canvas.drawCircle(width / 2, height / 2, width / 2 - 1, paint);
} else if (shapeType == 2) {
RectF rectf = new RectF(1, 1, getWidth() - 1, getHeight() - 1);
canvas.drawRoundRect(rectf, radius + 1, radius + 1, paint);
}
paint.setXfermode(xfermode);
float scaleWidth = ((float) getWidth()) / bitmap.getWidth();
float scaleHeight = ((float) getHeight()) / bitmap.getHeight();
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
//bitmap scale
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
canvas.drawBitmap(bitmap, 0, 0, paint);
canvas.restore();
}
/**
* draw the effect when pressed
*
* @param canvas
*/
private void drawPress(Canvas canvas) {
// check is rectangle or circle
if (shapeType == 1) {
canvas.drawCircle(width / 2, height / 2, width / 2 - 1, pressPaint);
} else if (shapeType == 2) {
RectF rectF = new RectF(1, 1, width - 1, height - 1);
canvas.drawRoundRect(rectF, radius + 1, radius + 1, pressPaint);
}
}
/**
* draw customized border
*
* @param canvas
*/
private void drawBorder(Canvas canvas) {
if (borderWidth > 0) {
Paint paint = new Paint();
paint.setStrokeWidth(borderWidth);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(borderColor);
paint.setAntiAlias(true);
// // check is rectangle or circle
if (shapeType == 1) {
canvas.drawCircle(width / 2, height / 2, (width - borderWidth) / 2, paint);
} else if (shapeType == 2) {
RectF rectf = new RectF(borderWidth / 2, borderWidth / 2, getWidth() - borderWidth / 2,
getHeight() - borderWidth / 2);
canvas.drawRoundRect(rectf, radius, radius, paint);
}
}
}
/**
* monitor the size change
*
* @param w
* @param h
* @param oldw
* @param oldh
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
}
/**
* monitor if touched
*
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
pressPaint.setAlpha(pressAlpha);
invalidate();
break;
case MotionEvent.ACTION_UP:
pressPaint.setAlpha(0);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
break;
default:
pressPaint.setAlpha(0);
invalidate();
break;
}
return super.onTouchEvent(event);
}
/**
* @param drawable
* @return
*/
private Bitmap getBitmapFromDrawable(Drawable drawable) {
if (drawable == null) {
return null;
}
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
Bitmap bitmap;
int width = Math.max(drawable.getIntrinsicWidth(), 2);
int height = Math.max(drawable.getIntrinsicHeight(), 2);
try {
bitmap = Bitmap.createBitmap(width, height, BITMAP_CONFIG);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
} catch (IllegalArgumentException e) {
e.printStackTrace();
bitmap = null;
}
return bitmap;
}
/**
* set border color
*
* @param borderColor
*/
public void setBorderColor(int borderColor) {
this.borderColor = borderColor;
invalidate();
}
/**
* set border width
*
* @param borderWidth
*/
public void setBorderWidth(int borderWidth) {
this.borderWidth = borderWidth;
}
/**
* set alpha when pressed
*
* @param pressAlpha
*/
public void setPressAlpha(int pressAlpha) {
this.pressAlpha = pressAlpha;
}
/**
* set color when pressed
*
* @param pressColor
*/
public void setPressColor(int pressColor) {
this.pressColor = pressColor;
}
/**
* set radius
*
* @param radius
*/
public void setRadius(int radius) {
this.radius = radius;
invalidate();
}
/**
* set shape,1 is circle, 2 is rectangle
*
* @param shapeType
*/
public void setShapeType(int shapeType) {
this.shapeType = shapeType;
invalidate();
}
}

View File

@@ -0,0 +1,33 @@
package io.agora.onetoone.utils
import android.text.TextUtils
import android.util.Log
import io.agora.media.RtcTokenBuilder
import io.agora.onetoone.BuildConfig
import kotlin.random.Random
/**
* @author create by zhangwei03
*/
object KeyCenter {
private const val TAG = "KeyCenter"
var rtcUid: Int = Random(System.nanoTime()).nextInt(10000) + 1000000;
fun getRtcToken(channelId: String, uid: Int): String {
var rtcToken: String = ""
if (TextUtils.isEmpty(BuildConfig.AG_APP_CERTIFICATE)) {
return rtcToken
}
try {
rtcToken = RtcTokenBuilder().buildTokenWithUid(
BuildConfig.AG_APP_ID, BuildConfig.AG_APP_CERTIFICATE, channelId, uid,
RtcTokenBuilder.Role.Role_Publisher, 0
)
} catch (e: Exception) {
Log.e(TAG, "rtc token build error:${e.message}")
}
return rtcToken
}
}

View File

@@ -0,0 +1,173 @@
package io.agora.onetoone.utils
import android.os.*
import com.netease.yunxin.kit.corekit.im.IMKitClient.getApplicationContext
import com.orhanobut.logger.*
import java.io.File
import java.io.FileWriter
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.*
object Ov1Logger {
private val entLogger = EntLogger(EntLogger.Config("Ov1"))
@JvmStatic
fun d(tag: String, message: String, vararg args: Any) {
entLogger.d(tag, message, args)
}
@JvmStatic
fun w(tag: String, message: String, vararg args: Any) {
entLogger.w(tag, message, args)
}
@JvmStatic
fun e(tag: String, message: String, vararg args: Any) {
entLogger.e(tag, message, args)
}
}
class EntLogger(private val config: Config) {
companion object {
private val LogFolder = getApplicationContext().getExternalFilesDir("")!!.absolutePath
private val LogFileWriteThread by lazy {
HandlerThread("AndroidFileLogger.$LogFolder").apply {
start()
}
}
}
data class Config(
val sceneName: String,
val fileSize: Int = 2 * 1024 * 1024, // 2M, unit in bytes
val fileName: String = "agora_${sceneName}_${SimpleDateFormat("yyyy-MM-DD", Locale.US).format(Date())}_log".lowercase()
)
private val dataFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.US)
init {
if (BuildConfig.DEBUG) {
Logger.addLogAdapter(
object: AndroidLogAdapter(
PrettyFormatStrategy.newBuilder()
.showThreadInfo(true) // (Optional) Whether to show thread info or not. Default true
.methodCount(1) // (Optional) How many method line to show. Default 2
.methodOffset(2) // (Optional) Hides internal method calls up to offset. Default 5
.logStrategy(LogcatLogStrategy()) // (Optional) Changes the log strategy to print out. Default LogCat
.tag(config.sceneName) // (Optional) Global tag for every log. Default PRETTY_LOGGER
.build()
){
override fun isLoggable(priority: Int, tag: String?): Boolean {
return tag == config.sceneName
}
}
)
}
Logger.addLogAdapter(
object: DiskLogAdapter(
CsvFormatStrategy
.newBuilder()
.logStrategy(DiskLogStrategy(WriteHandler()))
.tag(config.sceneName)
.build()
){
override fun isLoggable(priority: Int, tag: String?): Boolean {
return tag == config.sceneName
}
}
)
}
fun i(tag: String? = null, message: String, vararg args: Any) {
Logger.t(config.sceneName).i(formatMessage("INFO", tag, message), args)
}
fun w(tag: String? = null, message: String, vararg args: Any) {
Logger.t(config.sceneName).w(formatMessage("Warn", tag, message), args)
}
fun d(tag: String? = null, message: String, vararg args: Any) {
Logger.t(config.sceneName).d(formatMessage("Debug", tag, message), args)
}
fun e(tag: String? = null, message: String, vararg args: Any) {
Logger.t(config.sceneName).e(formatMessage("Error", tag, message), args)
}
fun e(tag: String? = null, throwable: Throwable, message: String, vararg args: Any) {
Logger.t(config.sceneName).e(throwable, formatMessage("Error", tag, message), args)
}
private fun formatMessage(level: String, tag: String?, message: String): String {
val sb = StringBuilder("[Agora][${level}][${config.sceneName}]")
tag?.let { sb.append("[${tag}]"); }
sb.append(" : (${dataFormat.format(Date())}) : $message")
return sb.toString()
}
private inner class WriteHandler : Handler(LogFileWriteThread.looper) {
override fun handleMessage(msg: Message) {
val content = msg.obj as String
var fileWriter: FileWriter? = null
val logFile = getLogFile(LogFolder, config.fileName)
try {
fileWriter = FileWriter(logFile, true)
writeLog(fileWriter, content)
fileWriter.flush()
fileWriter.close()
} catch (e: IOException) {
if (fileWriter != null) {
try {
fileWriter.flush()
fileWriter.close()
} catch (e1: IOException) { /* fail silently */
}
}
}
}
@Throws(IOException::class)
private fun writeLog(fileWriter: FileWriter, content: String) {
var writeContent = content
val agoraTag = writeContent.indexOf("[Agora]")
if (agoraTag > 0) {
writeContent = writeContent.substring(agoraTag)
}
fileWriter.append(writeContent)
}
private fun getLogFile(folderName: String, fileName: String): File {
val folder = File(folderName)
if (!folder.exists()) {
folder.mkdirs()
}
var newFileCount = 0
var newFile: File
var existingFile: File? = null
newFile = File(folder, getLogFileFullName(fileName, newFileCount))
while (newFile.exists()) {
existingFile = newFile
newFileCount++
newFile = File(folder, getLogFileFullName(fileName, newFileCount))
}
if (existingFile != null && existingFile.length() < config.fileSize) {
return existingFile
} else {
return newFile
}
}
private fun getLogFileFullName(fileName: String, count: Int) : String{
if(count == 0){
return "${fileName}.txt"
}
return "${fileName}_${count}.txt"
}
}
}

View File

@@ -0,0 +1,144 @@
package io.agora.onetoone.utils
import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.provider.Settings
import androidx.activity.ComponentActivity
import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
class PermissionHelp constructor(val activity: ComponentActivity) {
private var granted: (() -> Unit)? = null
private var unGranted: (() -> Unit)? = null
private val requestPermissionLauncher =
activity.registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
val granted = granted
val unGranted = unGranted
this.granted = null
this.unGranted = null
if (isGranted) {
granted?.invoke()
} else {
unGranted?.invoke()
}
}
private val appSettingLauncher =
activity.registerForActivityResult(object : ActivityResultContract<String, Boolean>() {
private var input: String? = null
override fun createIntent(context: Context, input: String): Intent {
this.input = input
return Intent().apply {
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
data = Uri.parse("package:" + context.packageName)
}
}
override fun parseResult(resultCode: Int, intent: Intent?): Boolean {
return ContextCompat.checkSelfPermission(
activity,
input ?: ""
) == PackageManager.PERMISSION_GRANTED
}
}) { isGranted ->
val granted = granted
val unGranted = unGranted
this.granted = null
this.unGranted = null
if (isGranted) {
granted?.invoke()
} else {
unGranted?.invoke()
}
}
/**
* Check camera and microphone permissions
*
* @param force If true, will redirect to the system app permission settings page if the permission is denied
*/
fun checkCameraAndMicPerms(granted: () -> Unit, unGranted: () -> Unit, force: Boolean = false) {
checkCameraPerm({
checkMicPerm(granted, unGranted, force)
}, unGranted, force)
}
/**
* Check microphone permission
*
* @param force If true, will redirect to the system app permission settings page if the permission is denied
*/
fun checkMicPerm(granted: () -> Unit, unGranted: () -> Unit, force: Boolean = false) {
checkPermission(Manifest.permission.RECORD_AUDIO, granted, force, unGranted)
}
/**
* Check camera permission
*
* @param force If true, will redirect to the system app permission settings page if the permission is denied
*/
fun checkCameraPerm(granted: () -> Unit, unGranted: () -> Unit, force: Boolean = false) {
checkPermission(Manifest.permission.CAMERA, granted, force, unGranted)
}
/**
* Check external storage permission
*
* @param force If true, will redirect to the system app permission settings page if the permission is denied
*/
fun checkStoragePerm(granted: () -> Unit, unGranted: () -> Unit, force: Boolean = false) {
checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, {
checkPermission(Manifest.permission.READ_EXTERNAL_STORAGE, granted, force, unGranted)
}, force, unGranted)
}
private fun checkPermission(perm: String, granted: () -> Unit, force: Boolean, unGranted: () -> Unit) {
when {
ContextCompat.checkSelfPermission(
activity,
perm
) == PackageManager.PERMISSION_GRANTED -> {
// You can use the API that requires the permission.
granted.invoke()
}
activity.shouldShowRequestPermissionRationale(perm) -> {
// In an educational UI, explain to the user why your app requires this
// permission for a specific feature to behave as expected, and what
// features are disabled if it's declined. In this UI, include a
// "cancel" or "no thanks" button that lets the user continue
// using your app without granting the permission.
// showInContextUI(...)
if (force) {
launchAppSetting(perm, granted, unGranted)
} else {
unGranted.invoke()
}
}
else -> {
// You can directly ask for the permission.
// The registered ActivityResultCallback gets the result of this request.
launchPermissionRequest(perm, granted, unGranted)
}
}
}
private fun launchPermissionRequest(perm: String, granted: () -> Unit, unGranted: () -> Unit) {
this.granted = granted
this.unGranted = unGranted
requestPermissionLauncher.launch(perm)
}
private fun launchAppSetting(perm: String, granted: () -> Unit, unGranted: () -> Unit) {
this.granted = granted
this.unGranted = unGranted
appSettingLauncher.launch(perm)
}
}

View File

@@ -0,0 +1,133 @@
package io.agora.onetoone.utils;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import com.netease.yunxin.kit.corekit.im.IMKitClient;
public class SPUtil {
private final static String PREFERENCES_NAME = "PREF_ONE_TO_ONE";
private SPUtil() {
}
private static final class MInstanceHolder {
static final SharedPreferences mInstance = IMKitClient.getApplicationContext().getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE);
}
/**
* Get the SharedPreferences instance object
*/
private static SharedPreferences getSharedPreference() {
return MInstanceHolder.mInstance;
}
/**
* Save a Boolean value!
*/
public static boolean putBoolean(String key, Boolean value) {
SharedPreferences sharedPreference = getSharedPreference();
Editor editor = sharedPreference.edit();
editor.putBoolean(key, value);
return editor.commit();
}
/**
* Save an int value!
*/
public static boolean putInt(String key, int value) {
SharedPreferences sharedPreference = getSharedPreference();
Editor editor = sharedPreference.edit();
editor.putInt(key, value);
return editor.commit();
}
/**
* Save a float value!
*/
public static boolean putFloat(String key, float value) {
SharedPreferences sharedPreference = getSharedPreference();
Editor editor = sharedPreference.edit();
editor.putFloat(key, value);
return editor.commit();
}
/**
* Save a long value!
*/
public static boolean putLong(String key, long value) {
SharedPreferences sharedPreference = getSharedPreference();
Editor editor = sharedPreference.edit();
editor.putLong(key, value);
return editor.commit();
}
/**
* Save a String value!
*/
public static boolean putString(String key, String value) {
SharedPreferences sharedPreference = getSharedPreference();
Editor editor = sharedPreference.edit();
editor.putString(key, value);
return editor.commit();
}
/**
* Get the String value
*/
public static String getString(String key, String defValue) {
SharedPreferences sharedPreference = getSharedPreference();
return sharedPreference.getString(key, defValue);
}
/**
* Get the int value
*/
public static int getInt(String key, int defValue) {
SharedPreferences sharedPreference = getSharedPreference();
return sharedPreference.getInt(key, defValue);
}
/**
* Get the float value
*/
public static float getFloat(String key, Float defValue) {
SharedPreferences sharedPreference = getSharedPreference();
return sharedPreference.getFloat(key, defValue);
}
/**
* Get the boolean value
*/
public static boolean getBoolean(String key, Boolean defValue) {
SharedPreferences sharedPreference = getSharedPreference();
return sharedPreference.getBoolean(key, defValue);
}
/**
* Get the long value
*/
public static long getLong(String key, long defValue) {
SharedPreferences sharedPreference = getSharedPreference();
return sharedPreference.getLong(key, defValue);
}
public static void removeKey(String key) {
try {
SharedPreferences sharedPreference = getSharedPreference();
Editor editor = sharedPreference.edit();
editor.remove(key);
editor.apply();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static void clear() {
SharedPreferences sharedPreference = getSharedPreference();
Editor editor = sharedPreference.edit();
editor.clear();
editor.apply();
}
}

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 815 B

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">
<solid android:color="@color/white" />
<corners
android:topLeftRadius="20dp"
android:topRightRadius="20dp" />
</shape>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="270"
android:endColor="#A1ADEA"
android:startColor="#C2A1EA"
android:useLevel="false"/>
</shape>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

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

View File

@@ -0,0 +1,184 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/default_room_bg"
tools:context=".LivingActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/vertical40"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.4" />
<FrameLayout
android:id="@+id/vCenter"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black_50"/>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/horizontal20"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.2" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tvTargetUid"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginStart="20dp"
android:layout_marginTop="10dp"
android:text="@string/pure_1v1_target_uid"
android:gravity="center_vertical"
android:textColor="#CCFFFFFF"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/horizontal20" />
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/etTargetUid"
android:layout_width="200dp"
android:layout_height="48dp"
android:layout_marginStart="16dp"
android:hint="@string/pure_1v1_target_uid_hint"
android:singleLine="true"
android:imeOptions="actionDone|flagNoExtractUi"
android:textColor="@color/white"
android:textColorHint="#99FFFFFF"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="@id/tvTargetUid"
app:layout_constraintStart_toEndOf="@id/tvTargetUid"
app:layout_constraintTop_toTopOf="@id/tvTargetUid" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/vertical50"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<FrameLayout
android:id="@+id/vLeft"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@android:color/white"
app:layout_constraintDimensionRatio="1.31:2"
app:layout_constraintEnd_toEndOf="@id/vertical50"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/horizontal20" />
<FrameLayout
android:id="@+id/vRight"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@android:color/white"
app:layout_constraintDimensionRatio="1.31:2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/vertical50"
app:layout_constraintTop_toTopOf="@id/horizontal20" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvCurrentId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:textColor="@color/white"
android:textSize="16sp"
android:layout_marginTop="25dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/pure_1v1_current_uid" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/btnQuitChannel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:layout_marginEnd="16dp"
android:padding="16dp"
android:src="@drawable/app_icon_exit"
android:text="@string/app_quit_channel"
app:layout_constraintBottom_toBottomOf="@id/tvCurrentId"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tvCurrentId" />
<include
android:id="@+id/statisticLayout"
layout="@layout/layout_statistic"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:visibility="invisible"
app:layout_constraintStart_toStartOf="@id/vertical40"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tvCurrentId" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnCall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="60dp"
android:text="@string/pure_1v1_invite"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnHangUp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="60dp"
android:text="@string/pure_1v1_hangup"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnAudio"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="30dp"
android:text="@string/pure_1v1_audio_off"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@+id/btnHangUp"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnVideo"
android:layout_width="120dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginTop="10dp"
android:text="@string/pure_1v1_video_off"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@+id/btnAudio"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/tvText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="11sp"
android:textColor="@color/white"
android:text=""
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/vLeft"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,324 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/default_room_bg">
<RelativeLayout
android:id="@+id/calll_wait_ry"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent">
<io.agora.onetoone.utils.CircleImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginTop="148dp"
app:es_shape_type="round"
android:id="@+id/user_iv"
android:src="@mipmap/ic_launcher_round"
app:ease_border_width="1dp"
app:ease_border_color="@color/white"
android:layout_centerHorizontal="true"/>
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/etTargetUid"
android:layout_width="200dp"
android:layout_height="48dp"
android:gravity="center"
android:layout_below="@id/user_iv"
android:layout_marginStart="16dp"
android:layout_centerHorizontal="true"
android:hint="@string/pure_1v1_target_uid_hint"
android:singleLine="true"
android:background="@null"
android:enabled="false"
android:imeOptions="actionDone|flagNoExtractUi"
android:textColor="@color/white"
android:textColorHint="#99FFFFFF"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="等待接听..."
android:layout_below="@id/etTargetUid"
android:textColor="#ffffffff"
android:textSize="16sp"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="60dp"
android:layout_alignParentBottom="true"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_weight="1"
android:drawablePadding="10dp"
android:drawableTop="@mipmap/kaiqimaikefeng"
android:text="开启麦克风"
android:id="@+id/btnAudio"
android:textColor="#ffffffff"
android:textSize="16sp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_weight="1"
android:drawablePadding="10dp"
android:drawableTop="@mipmap/guanduan_img"
android:text="取消"
android:id="@+id/btnQuitChannel"
android:textColor="#ffffffff"
android:textSize="16sp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_weight="1"
android:drawablePadding="10dp"
android:drawableTop="@mipmap/kaiqiyangshengqi"
android:text="开启扬声器"
android:textColor="#ffffffff"
android:textSize="16sp"
/>
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:id="@+id/calll_wait_in_ry"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent">
<io.agora.onetoone.utils.CircleImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginTop="148dp"
app:es_shape_type="round"
android:id="@+id/user_iv1"
android:src="@mipmap/ic_launcher_round"
app:ease_border_width="1dp"
app:ease_border_color="@color/white"
android:layout_centerHorizontal="true"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/etTargetUid2"
android:layout_width="200dp"
android:layout_height="48dp"
android:layout_below="@id/user_iv1"
android:layout_marginStart="16dp"
android:layout_centerHorizontal="true"
android:hint="@string/pure_1v1_target_uid_hint"
android:singleLine="true"
android:gravity="center"
android:background="@null"
android:imeOptions="actionDone|flagNoExtractUi"
android:textColor="@color/white"
android:textColorHint="#99FFFFFF"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="等待接听..."
android:layout_below="@id/etTargetUid2"
android:textColor="#ffffffff"
android:textSize="16sp"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="60dp"
android:layout_alignParentBottom="true"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_weight="1"
android:drawablePadding="10dp"
android:drawableTop="@mipmap/guanduan_img"
android:text="拒绝"
android:id="@+id/btnGuaduan"
android:textColor="#ffffffff"
android:textSize="16sp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_weight="1"
android:drawablePadding="10dp"
android:drawableTop="@mipmap/jietong_img"
android:text="接听"
android:id="@+id/btnJieTong"
android:textColor="#ffffffff"
android:textSize="16sp"
/>
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:id="@+id/video_ry"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/default_room_bg"
android:visibility="gone"
>
<FrameLayout
android:id="@+id/vLeft"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintStart_toStartOf="parent" />
<FrameLayout
android:id="@+id/vRight"
android:layout_width="120dp"
android:layout_height="192dp"
android:layout_marginTop="98dp"
android:background="@android:color/white"
android:layout_marginStart="15dp"
app:layout_constraintEnd_toEndOf="parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="60dp"
android:layout_alignParentBottom="true"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_weight="1"
android:drawablePadding="10dp"
android:drawableTop="@mipmap/kaiqimaikefeng"
android:text="开启麦克风"
android:id="@+id/btnAudio1"
android:textColor="#ffffffff"
android:textSize="16sp"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_weight="1"
android:drawablePadding="10dp"
android:drawableTop="@mipmap/yangshengqi_img"
android:text="开启扬声器"
android:textColor="#ffffffff"
android:textSize="16sp"
/>
<TextView
android:id="@+id/btnVideo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_weight="1"
android:drawablePadding="10dp"
android:drawableTop="@mipmap/kaiqiyangshengqi"
android:text="@string/pure_1v1_video_off"
android:textColor="#ffffffff"
android:textSize="16sp"
/>
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginTop="20dp"
android:layout_weight="1"
android:drawablePadding="10dp"
android:drawableTop="@mipmap/guanduan_img"
android:text="取消"
android:id="@+id/btnHangUp"
android:textColor="#ffffffff"
android:textSize="16sp"
/>
</LinearLayout>
</RelativeLayout>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvCurrentId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:textColor="@color/white"
android:textSize="16sp"
android:visibility="gone"
android:layout_marginTop="25dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/pure_1v1_current_uid" />
<include
android:id="@+id/statisticLayout"
layout="@layout/layout_statistic"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tvCurrentId" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnCall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="60dp"
android:text="@string/pure_1v1_invite"
android:visibility="gone"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<TextView
android:id="@+id/tvText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="11sp"
android:textColor="@color/white"
android:text=""
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</RelativeLayout>

View File

@@ -0,0 +1,232 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:paddingBottom="24dp"
android:background="@drawable/bottom_light_dialog_bg"
tools:theme="@style/Theme.MaterialComponents"
android:fitsSystemWindows="false">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/vertical50"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/ivBack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:padding="16dp"
android:src="@drawable/app_icon_back"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_debugging_title"
android:textColor="@color/black"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/ivBack"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/ivBack" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tvSure"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:paddingVertical="12dp"
android:text="@string/app_confirm"
android:textColor="#009FFF"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/ivBack"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/ivBack" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layoutResolution"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginHorizontal="16dp"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/ivBack">
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_debug_encode_dimensions"
android:textColor="#303553"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/etResolutionWidth"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:gravity="center"
android:imeOptions="actionNext"
android:inputType="numberDecimal"
android:textColor="#303553"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/tvResolutionX"
app:layout_constraintTop_toTopOf="parent"
tools:text="1920" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tvResolutionX"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_debug_x"
android:textColor="#303553"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/etResolutionHeight"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/etResolutionHeight"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:gravity="center"
android:imeOptions="actionNext"
android:inputType="numberDecimal"
android:singleLine="true"
android:textColor="#303553"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="1080" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/color_deliver"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layoutFrameRate"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginHorizontal="16dp"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/layoutResolution">
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_debug_encode_frame_rate"
android:textColor="#303553"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/etFps"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:gravity="center"
android:imeOptions="actionNext"
android:inputType="numberDecimal"
android:singleLine="true"
android:textColor="#303553"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/tvFps"
app:layout_constraintTop_toTopOf="parent"
tools:text="60" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tvFps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_debug_frame_rate"
android:textColor="#303553"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/color_deliver"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/layoutBitrate"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginHorizontal="16dp"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/layoutFrameRate">
<com.google.android.material.textview.MaterialTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_debug_encode_bitrate"
android:textColor="#303553"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/etBitrate"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:gravity="center"
android:imeOptions="actionNext"
android:inputType="numberDecimal"
android:singleLine="true"
android:textColor="#303553"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/tvBitrate"
app:layout_constraintTop_toTopOf="parent"
tools:text="6000" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tvBitrate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_debug_bitrate"
android:textColor="#303553"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/color_deliver"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/statistic_bg"
android:paddingHorizontal="20dp"
android:paddingVertical="10dp">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/tvStatistic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableEnd="@drawable/app_icon_switch"
android:drawablePadding="2dp"
android:text="@string/app_statistic"
android:textColor="@color/white"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TableLayout
android:id="@+id/tlStatistic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible"
app:layout_constraintTop_toBottomOf="@id/tvStatistic"
tools:visibility="visible">
<TableRow >
<TextView
android:id="@+id/tvEncodeDimensions"
android:layout_weight="1"
android:textColor="@color/white"
android:textSize="12sp"
android:paddingVertical="3dp"
tools:text="@string/app_statistic_dimensions" />
</TableRow>
<TableRow >
<TextView
android:id="@+id/tvEncodeFrameRate"
android:layout_weight="1"
android:textColor="@color/white"
android:textSize="12sp"
android:paddingVertical="3dp"
tools:text="@string/app_statistic_frame_rate" />
</TableRow>
<TableRow >
<TextView
android:id="@+id/tvEncodeBitrate"
android:layout_weight="1"
android:textColor="@color/white"
android:textSize="12sp"
android:paddingVertical="3dp"
tools:text="@string/app_statistic_bitrate" />
</TableRow>
<TableRow >
<TextView
android:id="@+id/tvLocalUid"
android:layout_weight="1"
android:paddingVertical="3dp"
android:textColor="@color/white"
android:textSize="12sp"
tools:text="@string/app_local_uid" />
</TableRow>
<TableRow >
<TextView
android:id="@+id/tvPkChannels"
android:layout_weight="1"
android:textColor="@color/white"
android:textSize="12sp"
android:paddingVertical="3dp"
tools:text="@string/app_pk_channels" />
</TableRow>
</TableLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/video_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black_50"/>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/video_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -0,0 +1,3 @@
<resources>
<dimen name="fab_margin">48dp</dimen>
</resources>

View File

@@ -0,0 +1,16 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.OneToOne" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/black</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -0,0 +1,3 @@
<resources>
<dimen name="fab_margin">200dp</dimen>
</resources>

View File

@@ -0,0 +1,3 @@
<resources>
<dimen name="fab_margin">48dp</dimen>
</resources>

View File

@@ -0,0 +1,106 @@
<resources>
<string name="app_name">遇聊</string>
<string name="app_channel_name">&#8194;&#8194;</string>
<string name="app_role">&#12288;&#12288;</string>
<string name="app_input_channel">输入直播频道名</string>
<string name="app_input_title_local_uid">当前的用户id</string>
<string name="app_input_local_uid">输入当前的用uid</string>
<string name="app_role_broadcaster">主播</string>
<string name="app_role_audience">观众</string>
<string name="app_join_channel">加入频道</string>
<string name="app_debugging">Debug页面</string>
<string name="app_debugging_title">开发者模式设置</string>
<string name="app_debug_encode_dimensions">编码分辨率</string>
<string name="app_debug_encode_frame_rate">编码帧率</string>
<string name="app_debug_encode_bitrate">码率</string>
<string name="app_debug_x">x</string>
<string name="app_debug_frame_rate">fps</string>
<string name="app_debug_bitrate">kbps</string>
<string name="app_confirm">确认</string>
<string name="app_pk_channel">对端频道</string>
<string name="app_update_pk_channel">更新对端频道号</string>
<string name="app_quit_channel">退出</string>
<string name="app_statistic">实时数据</string>
<string name="app_statistic_dimensions">编码分辨率: %s</string>
<string name="app_statistic_frame_rate">编码帧率: %s fps</string>
<string name="app_statistic_bitrate">编码码率: %s Kbps</string>
<string name="app_pk_channels">对端频道:%s</string>
<string name="app_local_uid">UID%s</string>
<string name="app_main_channel_empty">加入的频道不能为空</string>
<!-- Radio Buttons -->
<string name="radio_rtm_show_1v1">RTM秀转场1V1</string>
<string name="radio_rtm_pure_1v1">RTM纯1V1</string>
<string name="radio_easemob_show_1v1">环信秀转场1V1</string>
<string name="radio_easemob_pure_1v1">环信纯1V1</string>
<!-- Input Fields -->
<string name="input_broadcaster_id">主播id</string>
<string name="input_broadcaster_id_hint">输入主播id</string>
<!-- Checkboxes -->
<string name="checkbox_auto_accept">收到呼叫自动接受</string>
<string name="checkbox_first_frame">音视频首帧与接通相关</string>
<string name="checkbox_join_rtc">提前加入RTC频道</string>
<!-- Toast Messages -->
<string name="toast_no_im_app_key">未配置IM_APP_KEY</string>
<string name="toast_user_id_empty">用户id不能为空</string>
<string name="toast_local_user_id_number">本地用户的id需要是纯数字</string>
<string name="toast_both_user_id_number">本地和远端用户的id需要是纯数字</string>
<string name="toast_get_token_failed">get token failed</string>
<!-- Button Text -->
<string name="btn_create_show_to_1v1">创建秀场转1v1</string>
<string name="btn_join_show_to_1v1">加入秀场转1v1</string>
<string name="btn_enter_pure_1v1">进入纯1v1</string>
<!-- Channel -->
<string name="app_current_channel">当前频道: %s</string>
<string name="label_current_user_id">当前用户id%1$s</string>
<!-- Call Type Dialog -->
<string name="call_type_dialog_title">通话类型选择</string>
<string name="call_type_dialog_message">选择音频或视频通话</string>
<string name="call_type_audio">音频</string>
<string name="call_type_video">视频</string>
<!-- Alert Dialog -->
<string name="alert_title">提示</string>
<string name="alert_calling_user">呼叫用户 %d 中</string>
<string name="alert_cancel">取消</string>
<string name="alert_incoming_call">用户 %1$d 邀请您1对1通话</string>
<string name="alert_accept">同意</string>
<string name="alert_reject">拒绝</string>
<!-- Toast Messages -->
<string name="toast_no_permission">没给权限😯</string>
<string name="toast_call_api_initializing">CallAPi初始化中</string>
<string name="toast_no_target_user">无目标用户</string>
<string name="toast_rtm_connected">rtm已连接</string>
<string name="toast_rtm_disconnected">rtm已断开</string>
<string name="toast_easemob_connected">环信已连接</string>
<string name="toast_easemob_disconnected">环信已断开</string>
<string name="toast_rtm_not_logged_in">rtm未登录或连接异常</string>
<string name="toast_easemob_not_logged_in">环信未登录或连接异常</string>
<string name="toast_call_started">通话开始%s</string>
<string name="toast_call_ended">通话结束</string>
<string name="toast_call_rejected">通话被拒绝</string>
<string name="toast_no_answer">无应答</string>
<string name="toast_user_busy">用户正忙</string>
<!-- Call Status -->
<string name="call_status_start">通话开始,\nRTC 频道号: %1$s,\n主叫用户id: %2$d,\n当前用户id: %3$d,\n开始时间戳: %4$d ms</string>
<string name="call_status_end">通话结束,\nRTC 频道号: %1$s,\n挂断用户id: %2$d,\n当前用户id: %3$d,\n结束时间戳: %4$d ms\n通话时长: %5$d ms</string>
<!-- Pure 1v1 Activity -->
<string name="pure_1v1_target_uid">目标用户uid</string>
<string name="pure_1v1_target_uid_hint">输入目标用户uid</string>
<string name="pure_1v1_current_uid">当前用户id</string>
<string name="pure_1v1_invite">邀请</string>
<string name="pure_1v1_hangup">挂断</string>
<string name="pure_1v1_video_on">开启视频</string>
<string name="pure_1v1_video_off">关闭视频</string>
<string name="pure_1v1_audio_on">开启音频</string>
<string name="pure_1v1_audio_off">关闭音频</string>
</resources>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF7800FF</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="color_deliver">#EFEFEF</color>
<color name="black_10">#1A000000</color>
<color name="black_15">#26000000</color>
<color name="black_20">#33000000</color>
<color name="black_25">#40000000</color>
<color name="black_30">#4D000000</color>
<color name="black_35">#59000000</color>
<color name="black_40">#66000000</color>
<color name="black_45">#73000000</color>
<color name="black_50">#80000000</color>
<color name="black_55">#8C000000</color>
<color name="black_60">#99000000</color>
<color name="black_65">#A6000000</color>
<color name="black_70">#B3000000</color>
<color name="black_75">#BF000000</color>
<color name="black_80">#CC000000</color>
<color name="black_85">#D9000000</color>
<color name="black_90">#E6000000</color>
<color name="black_95">#F2000000</color>
</resources>

View File

@@ -0,0 +1,3 @@
<resources>
<dimen name="fab_margin">16dp</dimen>
</resources>

View File

@@ -0,0 +1,106 @@
<resources>
<string name="app_name">OneToOne</string>
<string name="app_channel_name">Channel Number</string>
<string name="app_role">Role</string>
<string name="app_input_channel">Enter Live Channel Name</string>
<string name="app_input_title_local_uid">Current User ID</string>
<string name="app_input_local_uid">Enter Current User UID</string>
<string name="app_role_broadcaster">Broadcaster</string>
<string name="app_role_audience">Audience</string>
<string name="app_join_channel">Join Channel</string>
<string name="app_debugging">Debug Page</string>
<string name="app_debugging_title">Developer Mode Settings</string>
<string name="app_debug_encode_dimensions">Encoding Resolution</string>
<string name="app_debug_encode_frame_rate">Encoding Frame Rate</string>
<string name="app_debug_encode_bitrate">Bitrate</string>
<string name="app_debug_x">x</string>
<string name="app_debug_frame_rate">fps</string>
<string name="app_debug_bitrate">kbps</string>
<string name="app_confirm">Confirm</string>
<string name="app_pk_channel">Peer Channel</string>
<string name="app_update_pk_channel">Update Peer Channel Number</string>
<string name="app_quit_channel">Exit</string>
<string name="app_statistic">Real-time Data</string>
<string name="app_statistic_dimensions">Encoding Resolution: %s</string>
<string name="app_statistic_frame_rate">Encoding Frame Rate: %s fps</string>
<string name="app_statistic_bitrate">Encoding Bitrate: %s Kbps</string>
<string name="app_pk_channels">Peer Channel: %s</string>
<string name="app_local_uid">UID: %s</string>
<string name="app_main_channel_empty">Joined channel cannot be empty</string>
<!-- Radio Buttons -->
<string name="radio_rtm_show_1v1">RTM Show to 1v1</string>
<string name="radio_rtm_pure_1v1">RTM Pure 1v1</string>
<string name="radio_easemob_show_1v1">Easemob Show to 1v1</string>
<string name="radio_easemob_pure_1v1">Easemob Pure 1v1</string>
<!-- Input Fields -->
<string name="input_broadcaster_id">Broadcaster ID</string>
<string name="input_broadcaster_id_hint">Enter broadcaster ID</string>
<!-- Checkboxes -->
<string name="checkbox_auto_accept">Auto accept incoming calls</string>
<string name="checkbox_first_frame">First frame related to connection</string>
<string name="checkbox_join_rtc">Join RTC channel in advance</string>
<!-- Toast Messages -->
<string name="toast_no_im_app_key">IM_APP_KEY not configured</string>
<string name="toast_user_id_empty">User ID cannot be empty</string>
<string name="toast_local_user_id_number">Local user ID must be numeric</string>
<string name="toast_both_user_id_number">Both local and remote user IDs must be numeric</string>
<string name="toast_get_token_failed">Get token failed</string>
<!-- Button Text -->
<string name="btn_create_show_to_1v1">Create Show to 1v1</string>
<string name="btn_join_show_to_1v1">Join Show to 1v1</string>
<string name="btn_enter_pure_1v1">Enter Pure 1v1</string>
<!-- Channel -->
<string name="app_current_channel">Current Channel: %s</string>
<string name="label_current_user_id">Current User ID: %1$s</string>
<!-- Call Type Dialog -->
<string name="call_type_dialog_title">Call Type Selection</string>
<string name="call_type_dialog_message">Select audio or video call</string>
<string name="call_type_audio">Audio</string>
<string name="call_type_video">Video</string>
<!-- Alert Dialog -->
<string name="alert_title">Notice</string>
<string name="alert_calling_user">Calling user %d</string>
<string name="alert_cancel">Cancel</string>
<string name="alert_incoming_call">User %1$d invites you to 1-on-1 call</string>
<string name="alert_accept">Accept</string>
<string name="alert_reject">Reject</string>
<!-- Toast Messages -->
<string name="toast_no_permission">No permission granted 😯</string>
<string name="toast_call_api_initializing">CallAPI Initializing</string>
<string name="toast_no_target_user">No Target User</string>
<string name="toast_rtm_connected">RTM connected</string>
<string name="toast_rtm_disconnected">RTM disconnected</string>
<string name="toast_easemob_connected">Easemob Client Connected</string>
<string name="toast_easemob_disconnected">Easemob Client Disconnected</string>
<string name="toast_rtm_not_logged_in">RTM not logged in or connection abnormal</string>
<string name="toast_easemob_not_logged_in">Easemob not logged in or connection abnormal</string>
<string name="toast_call_started">Call started %s</string>
<string name="toast_call_ended">Call ended</string>
<string name="toast_call_rejected">Call rejected</string>
<string name="toast_no_answer">No answer</string>
<string name="toast_user_busy">User is busy</string>
<!-- Call Status -->
<string name="call_status_start">Call started,\nRTC Channel: %1$s,\nCaller ID: %2$d,\nCurrent User ID: %3$d,\nStart Timestamp: %4$d ms</string>
<string name="call_status_end">Call ended,\nRTC Channel: %1$s,\nHangup User ID: %2$d,\nCurrent User ID: %3$d,\nEnd Timestamp: %4$d ms,\nDuration: %5$d ms</string>
<!-- Pure 1v1 Activity -->
<string name="pure_1v1_target_uid">Target User ID</string>
<string name="pure_1v1_target_uid_hint">Enter target user ID</string>
<string name="pure_1v1_current_uid">Current User ID</string>
<string name="pure_1v1_invite">Invite 1v1</string>
<string name="pure_1v1_hangup">Hangup 1v1</string>
<string name="pure_1v1_video_on">Video On</string>
<string name="pure_1v1_video_off">Video Off</string>
<string name="pure_1v1_audio_on">Audio On</string>
<string name="pure_1v1_audio_off">Audio Off</string>
</resources>

View File

@@ -0,0 +1,40 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.OneToOne" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
<style name="Theme.OneToOne.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="Theme.OneToOne.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="Theme.OneToOne.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
<declare-styleable name="CircleImageView">
<attr name="ease_border_color" format="color" />
<attr name="ease_border_width" format="dimension" />
<attr name="ease_press_alpha" format="integer" />
<attr name="ease_press_color" format="color" />
<attr name="ease_radius" format="dimension" />
<attr name="es_shape_type" format="enum">
<enum name="none" value="0" />
<enum name="round" value="1" />
<enum name="rectangle" value="2" />
</attr>
</declare-styleable>
</resources>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older that API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>

View File

@@ -0,0 +1,17 @@
package io.agora.onetoone
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}