diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f0f71e2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+.idea
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..3543521
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..4d7b743
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,47 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.3"
+
+ defaultConfig {
+ applicationId "com.tianrun.siphone"
+ minSdkVersion 19
+// targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+ multiDexEnabled true
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ ndk{
+ abiFilters "armeabi"
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility = 1.8
+ targetCompatibility = 1.8
+ }
+
+}
+
+dependencies {
+ implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
+
+ implementation 'com.qmuiteam:qmui:2.0.0-alpha10'
+
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation 'com.google.android.material:material:1.2.0'
+ implementation 'androidx.gridlayout:gridlayout:1.0.0'
+ implementation files('libs/fastjson-1.1.36.jar')
+ testImplementation 'junit:junit:4.13'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+}
diff --git a/app/libs/alllibs-release.aar b/app/libs/alllibs-release.aar
new file mode 100644
index 0000000..e348005
Binary files /dev/null and b/app/libs/alllibs-release.aar differ
diff --git a/app/libs/fastjson-1.1.36.jar b/app/libs/fastjson-1.1.36.jar
new file mode 100644
index 0000000..0913888
Binary files /dev/null and b/app/libs/fastjson-1.1.36.jar differ
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..6e7ffa9
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -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
diff --git a/app/src/androidTest/java/cn/bluetel/bluetelsiptestdemo/ExampleInstrumentedTest.java b/app/src/androidTest/java/cn/bluetel/bluetelsiptestdemo/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..360479a
--- /dev/null
+++ b/app/src/androidTest/java/cn/bluetel/bluetelsiptestdemo/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package cn.bluetel.bluetelsiptestdemo;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("cn.bluetel.bluetelsiptestdemo", appContext.getPackageName());
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..9993973
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/tianrun/sipcall/App.java b/app/src/main/java/com/tianrun/sipcall/App.java
new file mode 100644
index 0000000..8826db9
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/App.java
@@ -0,0 +1,38 @@
+package com.tianrun.sipcall;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.multidex.MultiDexApplication;
+
+
+public class App extends MultiDexApplication {
+
+ public static String TAG = "ApplicationAPP";
+ private static App sInstance;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ sInstance = this;
+ }
+
+ public static App getInstance() {
+ return sInstance;
+ }
+
+ public static Context getContext() {
+ return sInstance;
+ }
+
+ public static String getRString(int resid) {
+ return sInstance.getString(resid);
+ }
+
+ @Override
+ public void onTerminate() {
+ super.onTerminate();
+ Log.e(TAG, "Application终止");
+ }
+
+}
diff --git a/app/src/main/java/com/tianrun/sipcall/BootBroadcastReceiver.java b/app/src/main/java/com/tianrun/sipcall/BootBroadcastReceiver.java
new file mode 100644
index 0000000..9e9ef32
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/BootBroadcastReceiver.java
@@ -0,0 +1,21 @@
+package com.tianrun.sipcall;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import com.tianrun.sipcall.login.LoginActivity;
+
+public class BootBroadcastReceiver extends BroadcastReceiver {
+ static final String ACTION = "android.intent.action.BOOT_COMPLETED";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if(intent.getAction().equals(ACTION)){
+ Intent myIntent = new Intent(context, LoginActivity.class);
+ myIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(myIntent);
+ }
+
+ }
+}
diff --git a/app/src/main/java/com/tianrun/sipcall/SipEngine.java b/app/src/main/java/com/tianrun/sipcall/SipEngine.java
new file mode 100644
index 0000000..209020e
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/SipEngine.java
@@ -0,0 +1,318 @@
+package com.tianrun.sipcall;
+
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.tianrun.sipcall.call.CallMediaUtils;
+import com.tianrun.sipcall.call.InCallActivity;
+import com.tianrun.sipcall.call.InCallMeetingActivity;
+import com.tianrun.sipcall.call.utils.InCallUtils;
+import com.tianrun.sipcall.net.Net;
+import com.tianrun.sipcall.ui.ActivityMgr;
+import com.tianrun.sipcall.ui.UIUtl;
+import com.tianrun.sipcall.utils.CONS;
+import com.tianrun.sipcall.utils.HttpUtl;
+import com.tianrun.sipcall.utils.logmy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import blue.all.BluetelEngine;
+import blue.bluetelobserver.BluetelInterface;
+
+
+public class SipEngine implements BluetelInterface {
+
+ public static final String TAG = "TestEngine";
+
+ private static SipEngine Instance;
+ public boolean onLine = false;
+ public boolean isRelogin = false;
+ private String name = "0";
+ private String pw = "0";
+ public String ip = "0";
+ private int port = 0;
+ public static List callPagesConfig = new ArrayList();//所有通话页面的集合
+
+ private BluetelEngine myBluetelEngine;
+
+ public static SipEngine getInstance() {
+ if (Instance == null)
+ Instance = new SipEngine();
+ return Instance;
+ }
+
+ //删除页面配置
+ private void removeConfig(int callid) {
+ //移除页面配置
+ InCallUtils cursorPagesConfig = null;
+ for (InCallUtils icu : callPagesConfig) {
+ if (icu.getCallId() == callid) {
+ cursorPagesConfig = icu;
+ }
+ }
+ if (cursorPagesConfig != null) {
+ callPagesConfig.remove(cursorPagesConfig);
+ }
+ }
+
+ /**
+ * 根据号码查通话的id
+ *
+ * @param callnumber
+ * @return
+ */
+ public int getCallId(String callnumber) {
+ int res = 0;
+ for (InCallUtils icu : callPagesConfig) {
+ if (icu.getCallNumber().equals(callnumber)) {
+ res = icu.getCallId();
+ break;
+ }
+ }
+ return res;
+ }
+
+
+ /**
+ * 退出
+ */
+ public void stop() {
+ myBluetelEngine.deinit();
+ }
+
+ public String getip() {
+ return ip;
+ }
+ public String getName() {return name;};
+
+ public boolean isonLine() {
+ return onLine;
+ }
+
+ public void init() {
+ if (myBluetelEngine == null) {
+ myBluetelEngine = new BluetelEngine(App.getContext(), this);
+ } else {
+ myBluetelEngine.Stop();
+ myBluetelEngine = new BluetelEngine(App.getContext(), this);
+ }
+ logmy.e("服务启动");
+ }
+
+
+ /**
+ * 注册
+ *
+ * @param name
+ * @param pw
+ * @param ip
+ * @param port
+ */
+ public void Register(String name, String pw, String ip, int port) {
+ this.name = name;
+ this.pw = pw;
+ this.ip = ip;
+ this.port = port;
+ myBluetelEngine.Register(name, pw, ip, port);
+ }
+
+ /**
+ * 注销
+ */
+ public void Unregister() {
+ onLine = false;
+ myBluetelEngine.Stop();
+ }
+
+
+ /**************************** 接口 *************************************************/
+
+ @Override
+ public void AccountState(String uri, boolean state) {
+ logmy.e(uri + "-->" + state);
+ if (state) {
+ if (!onLine) {
+ onLine = true;
+ Net.login(this.name, this.pw, new HttpUtl.CallBack() {
+ @Override
+ public void onRequestComplete(int cmd, String result, Object orgs) {
+ ActivityMgr.sendMsg(CONS.LOGIN, null);
+ }
+
+ @Override
+ public void onRequestError(int cmd, String result, Object orgs) {
+ UIUtl.toastI("取得token失败");
+ ActivityMgr.sendMsg(CONS.LOGINFAILED, null);
+ }
+ }, null);
+ }
+ } else {
+ if(!onLine && !isRelogin) {
+ UIUtl.toastI("Sip登陆失败");
+ ActivityMgr.sendMsg(CONS.LOGINFAILED, null);
+ }
+ }
+ isRelogin = false;
+ }
+
+ public boolean isMeetingCall(String phoneNo) {
+ if (phoneNo.length() >= 5) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void FirstIncoming(String incomingNumber, boolean isVideo, int rPort, int lPort, boolean isHolder, int callid) {
+ logmy.e("FirstIncoming:" + incomingNumber + "<>" + isVideo + "<>" + rPort + "<>" + lPort + "<>" + isHolder + "<>" + callid);
+ int calltype = isVideo == false ? 0 : 1;
+ String state = isVideo == true ? "视频来电" : "音频来电";
+ callPagesConfig.add(new InCallUtils(incomingNumber, isHolder, isVideo, rPort, lPort, incomingNumber, state, false, 0, callid));
+ GoToInCall(App.getContext(), incomingNumber, "来电", calltype, callid);
+ }
+
+ @Override
+ public void OtherIncoming(String incomingNumber, boolean isVideo, int rPort, int lPort, boolean isHolder, int callid) {
+ logmy.e("OtherIncoming:" + incomingNumber + "<>" + isVideo + "<>" + rPort + "<>" + lPort + "<>" + isHolder + "<>" + callid);
+ String state = isVideo == true ? "视频来电" : "音频来电";
+ callPagesConfig.add(new InCallUtils(incomingNumber, isHolder, isVideo, rPort, lPort, incomingNumber, state, false, 0, callid));
+ }
+
+ @Override
+ public void StateCallDown(int callid, String number) {
+ logmy.e(callid + "<>" + number);
+ removeConfig(callid);
+ ActivityMgr.sendMsg(CONS.CALLDOWN, callid);
+ }
+
+ @Override
+ public void CallState(int callid, int callstate, String number) {
+ String stateValues = "未知状态";
+ switch (callstate) {
+ case 1:
+ stateValues = "呼叫中";//也可以在这里启动主动呼叫界面(此demo主动启动页面做在了button上)
+ break;
+ case 2:
+ stateValues = "未知";
+ break;
+ case 3:
+ stateValues = "振铃中";
+ break;
+ case 4:
+ stateValues = "连接中";
+ break;
+ case 5:
+ stateValues = "通话中";
+ //一般在这里进行增益调节
+ break;
+ case 6:
+ stateValues = "挂断";
+ break;
+ }
+ logmy.e(callid + "<>" + stateValues + "<>" + number);
+
+ ActivityMgr.sendMsg(CONS.CALLSTATE, stateValues);
+ }
+
+ @Override
+ public void NotifyCallMediaState(int callid, int r, int l, int payload) {
+ logmy.e("NotifyCallMediaState" + callid + "<>" + r + "<>" + l + "<>" + payload);
+ ActivityMgr.sendMsg(CONS.MEDIASTATE, new CallMediaUtils(callid, payload, r, l));
+ }
+
+ @Override
+ public void ErrorMessage(String s) {
+ logmy.e(s);
+ }
+
+
+ /************************ 方法 ***************************************************/
+
+ /**
+ * 去通话页面 要在NotifyCallMediaState接口之前页面开好
+ *
+ * @param context
+ * @param callnumber
+ * @param callstate
+ * @param calltype
+ * @param callid
+ */
+ public void GoToInCall(Context context, String callnumber, String callstate, int calltype, int callid) {
+ Intent intent = null;
+ if (isMeetingCall(callnumber)) {
+ intent = new Intent(context, InCallMeetingActivity.class);
+ } else {
+ intent = new Intent(context, InCallActivity.class);
+ }
+ Bundle bundle = new Bundle();
+ bundle.putInt("callid", callid);
+ bundle.putString("callnumber", callnumber);
+ bundle.putString("callstate", callstate);
+ bundle.putInt("calltype", calltype);
+ intent.putExtras(bundle);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ }
+
+ /**
+ * 打电话
+ *
+ * @param number 呼叫的号码
+ * @param isVideo 是否是视频呼叫
+ * @return
+ */
+ public int CallNumber(String number, boolean isVideo) {
+ int callid = myBluetelEngine.CallNumber(number, ip, port, isVideo);
+ int calltype = isVideo ? 0 : 1;
+ GoToInCall(App.getContext(), number, "呼叫中", calltype, callid);
+ callPagesConfig.add(new InCallUtils(number, false, isVideo, 0, 0, number, "呼叫中", false, 0, callid));
+ return callid;
+ }
+
+ /**
+ * 挂断电话
+ *
+ * @param callid
+ */
+ public void hangup(int callid) {
+ myBluetelEngine.hangup(callid);
+ removeConfig(callid);//移除页面配置
+ }
+
+ /**
+ * 接听电话
+ *
+ * @param callid
+ */
+ public void answer(int callid) {
+ myBluetelEngine.answer(callid);
+ }
+
+
+ /**
+ * 通话增益调节
+ *
+ * @param callId
+ * @param tx
+ * @param rx
+ */
+ public void setAdjustAudio(int callId, int tx, int rx) {
+ myBluetelEngine.setAdjustAudio2(callId, tx, rx);
+ }
+
+ /**
+ * 通话保持
+ *
+ * @param isHolder
+ * @param callid
+ * @return
+ */
+ public boolean Holder(boolean isHolder, int callid) {
+ return myBluetelEngine.Holder(isHolder, callid);
+ }
+
+
+}
diff --git a/app/src/main/java/com/tianrun/sipcall/call/CallActivity.java b/app/src/main/java/com/tianrun/sipcall/call/CallActivity.java
new file mode 100644
index 0000000..910873f
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/call/CallActivity.java
@@ -0,0 +1,91 @@
+package com.tianrun.sipcall.call;
+
+import com.tianrun.sipcall.R;
+import com.tianrun.sipcall.SipEngine;
+import com.tianrun.sipcall.ui.TrBaseActivity;
+import com.tianrun.sipcall.utils.CONS;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Handler.Callback;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.GridView;
+import android.widget.ListAdapter;
+import android.widget.SimpleAdapter;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+public class CallActivity extends TrBaseActivity implements AdapterView.OnItemClickListener {
+ public static Handler handler_CallActivity;
+// private Handler handler = new Handler(this);
+
+ private String[] nums = {"1","2","3","4","5","6","7","8","9","*","0","#"};
+ private EditText et_callnumber;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_callactivity);
+ handler_CallActivity = handler;
+ et_callnumber = (EditText) findViewById(R.id.et_callnumber);
+ initNumKeyboard();
+ }
+
+ public void initNumKeyboard(){
+ ArrayAdapter adapter = new ArrayAdapter(this.getBaseContext(), android.R.layout.simple_expandable_list_item_1, nums);
+
+ GridView gv = findViewById(R.id.NumKeyBoard);
+ gv.setAdapter(adapter);
+ gv.setOnItemClickListener(this);
+ }
+
+ public void audiocall(View view) {
+ String callnumber = et_callnumber.getText().toString();
+ if (callnumber.isEmpty()) {
+ Toast.makeText(CallActivity.this, "号码不能为空", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ SipEngine.getInstance().CallNumber(callnumber, false);
+ }
+
+ public void videocall(View view) {
+ String callnumber = et_callnumber.getText().toString();
+ if (callnumber.isEmpty()) {
+ Toast.makeText(CallActivity.this, "号码不能为空", Toast.LENGTH_SHORT).show();
+ return;
+ }
+ SipEngine.getInstance().CallNumber(callnumber, true);
+ }
+
+ public void createmeeting(View view)
+ {
+ startActivity(new Intent(this, CreateMeetingActivity.class));
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ handler_CallActivity = null;
+ }
+ @Override
+ public boolean handleMessage(Message m) {
+ switch (m.what) {
+ case CONS.FINISH:
+ finish();
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ et_callnumber.setText(et_callnumber.getText()+nums[position]);
+ }
+}
diff --git a/app/src/main/java/com/tianrun/sipcall/call/CallMediaUtils.java b/app/src/main/java/com/tianrun/sipcall/call/CallMediaUtils.java
new file mode 100644
index 0000000..56c8d94
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/call/CallMediaUtils.java
@@ -0,0 +1,56 @@
+package com.tianrun.sipcall.call;
+
+public class CallMediaUtils {
+
+ /**
+ * 通话的id
+ */
+ private int callid;
+ /**
+ * payload
+ */
+ private int payload;
+ /**
+ * 本地端口
+ */
+ private int l;
+ /**
+ * 远程端口
+ */
+ private int r;
+
+
+ public CallMediaUtils(int callid, int payload, int r, int l) {
+ super();
+ this.callid = callid;
+ this.payload = payload;
+ this.l = l;
+ this.r = r;
+ }
+ public int getCallid() {
+ return callid;
+ }
+ public void setCallid(int callid) {
+ this.callid = callid;
+ }
+ public int getPayload() {
+ return payload;
+ }
+ public void setPayload(int payload) {
+ this.payload = payload;
+ }
+ public int getL() {
+ return l;
+ }
+ public void setL(int l) {
+ this.l = l;
+ }
+ public int getR() {
+ return r;
+ }
+ public void setR(int r) {
+ this.r = r;
+ }
+
+
+}
diff --git a/app/src/main/java/com/tianrun/sipcall/call/CreateGroupActivity.java b/app/src/main/java/com/tianrun/sipcall/call/CreateGroupActivity.java
new file mode 100644
index 0000000..5858d74
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/call/CreateGroupActivity.java
@@ -0,0 +1,239 @@
+package com.tianrun.sipcall.call;
+
+import android.content.DialogInterface;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.Message;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.GridView;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.alibaba.fastjson.JSONObject;
+import com.qmuiteam.qmui.skin.QMUISkinManager;
+import com.qmuiteam.qmui.widget.dialog.QMUIDialog;
+import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction;
+import com.qmuiteam.qmui.widget.dialog.QMUITipDialog;
+import com.tianrun.sipcall.R;
+import com.tianrun.sipcall.SipEngine;
+import com.tianrun.sipcall.db.DBUser;
+import com.tianrun.sipcall.net.Net;
+import com.tianrun.sipcall.ui.TrAdapter;
+import com.tianrun.sipcall.ui.TrBaseActivity;
+import com.tianrun.sipcall.ui.UIUtl;
+import com.tianrun.sipcall.utils.HttpUtl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class CreateGroupActivity extends TrBaseActivity {
+ TextView meetingTopic;
+ GridView gridUsers;
+ TrAdapter adapterUser;
+
+ List users = new ArrayList<>();
+ HashMap mapUsers = new HashMap<>();
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.creatgroup_view);
+
+ View rootView = findViewById(R.id.RelativeLayoutNewGroupRoot);
+ meetingTopic = rootView.findViewById(R.id.group_name);
+ gridUsers = rootView.findViewById(R.id.GridUser_select);
+
+ users.clear();
+ mapUsers.clear();
+ DBUser u = new DBUser("", "添加分机号", "");
+ u.isAddFlag = true;
+ users.add(u);
+
+ setList(null);
+ }
+
+ public class UserSelectViews {
+ public TextView textViewName;
+ public TextView textViewNum;
+ public ImageView imageViewStatus;
+ public Button buttonDel;
+ public CheckBox checkBox;
+ public ImageView getImageViewPhoneIcon;
+ public ImageView imageViewAddflag;
+ }
+
+ public void setList(List list) {
+ if (list != null) {
+ for (DBUser u : list) {
+ mapUsers.put(u.phone, u);
+ users.add(users.size() - 1, u);
+ }
+ }
+
+ if (adapterUser == null) {
+ adapterUser = UIUtl.setList(this, gridUsers, R.layout.list_item_user_select, users, new TrAdapter.Callback() {
+ @Override
+ public void initCallback(Object data, int position, View prefabView, ViewGroup parent) {
+ UserSelectViews views;
+ if (prefabView.getTag() == null) {
+ views = new UserSelectViews();
+ views.textViewName = prefabView.findViewById(R.id.textViewName);
+ views.textViewNum = prefabView.findViewById(R.id.textViewNum);
+ views.imageViewStatus = prefabView.findViewById(R.id.imageViewStatus);
+ views.buttonDel = prefabView.findViewById(R.id.buttonDel);
+ views.checkBox = prefabView.findViewById(R.id.checkBox);
+ views.getImageViewPhoneIcon = prefabView.findViewById(R.id.imageView3PhoneIcon);
+ views.imageViewAddflag = prefabView.findViewById(R.id.imageViewAddFlag);
+ prefabView.setTag(views);
+ } else {
+ views = (UserSelectViews) (prefabView.getTag());
+ }
+
+ DBUser user = (DBUser) data;
+
+ if (user.isAddFlag) {
+ views.imageViewAddflag.setVisibility(View.VISIBLE);
+ views.buttonDel.setVisibility(View.INVISIBLE);
+ views.checkBox.setVisibility(View.INVISIBLE);
+ views.getImageViewPhoneIcon.setVisibility(View.INVISIBLE);
+ } else {
+ views.imageViewAddflag.setVisibility(View.INVISIBLE);
+ views.buttonDel.setVisibility(View.VISIBLE);
+ views.checkBox.setVisibility(View.INVISIBLE);
+ views.getImageViewPhoneIcon.setVisibility(View.VISIBLE);
+ }
+ views.textViewName.setText(user.name);
+ views.textViewNum.setText(user.phone);
+ if (user.isOffline()) {
+ views.imageViewStatus.setColorFilter(Color.GRAY);
+ } else if (user.isOnline()) {
+ views.imageViewStatus.setColorFilter(Color.GREEN);
+ } else if (user.isBusy()) {
+ views.imageViewStatus.setColorFilter(Color.RED);
+ } else {
+ views.imageViewStatus.setColorFilter(Color.GRAY);
+ }
+ views.checkBox.setChecked(true);
+ views.checkBox.setClickable(false);
+// views.checkBox.setOnClickListener(new View.OnClickListener() {
+// @Override
+// public void onClick(View v) {
+// UserSelect d = (UserSelect) data;
+// d.isSelected = !d.isSelected;
+// adapterUser.notifyDataSetChanged();
+// refreshSelectedUsers();
+// }
+// });
+ views.buttonDel.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ users.remove(data);
+ mapUsers.remove(user.phone);
+ adapterUser.notifyDataSetChanged();
+ }
+ });
+ }
+
+ @Override
+ public void clickCallback(Object data, AdapterView> parent, View view, int position, long id) {
+ DBUser d = (DBUser) data;
+ if (d.isAddFlag) {
+ showUsersMultiChoiceDialog();
+ }
+ }
+ });
+ } else {
+ adapterUser.notifyDataSetChanged();
+ }
+ }
+
+ private void showUsersMultiChoiceDialog() {
+ List list = new ArrayList<>();
+ List listPhone = new ArrayList<>();
+ for (DBUser u : DBUser.allUser) {
+ if (mapUsers.get(u.phone) == null) {
+ listPhone.add(u.phone);
+ list.add(u.phone + "|" + u.name);
+ }
+ }
+ if(list.size() == 0) {
+ UIUtl.toastI("所有分机已经添加");
+ return;
+ }
+
+ final String[] items = new String[list.size()];
+ list.toArray(items);
+ final QMUIDialog.MultiCheckableDialogBuilder builder = new QMUIDialog.MultiCheckableDialogBuilder(this)
+ .setTitle("选择要加入的分机号")
+// .setCheckedItems(new int[]{1, 3})
+ .setSkinManager(QMUISkinManager.defaultInstance(this))
+ .addItems(items, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ }
+ });
+ builder.addAction("取消", new QMUIDialogAction.ActionListener() {
+ @Override
+ public void onClick(QMUIDialog dialog, int index) {
+ dialog.dismiss();
+ }
+ });
+ builder.addAction("确定", new QMUIDialogAction.ActionListener() {
+ @Override
+ public void onClick(QMUIDialog dialog, int index) {
+ int[] selectIndexs = builder.getCheckedItemIndexes();
+ if (selectIndexs.length > 0) {
+ List list = new ArrayList<>();
+ for (int i = 0; i < selectIndexs.length; i++) {
+ String phNum = listPhone.get(selectIndexs[i]);
+ list.add(DBUser.getUser(phNum));
+ }
+ setList(list);
+ }
+
+ dialog.dismiss();
+ }
+ });
+
+ int mCurrentDialogStyle = com.qmuiteam.qmui.R.style.QMUI_Dialog;
+ builder.create(mCurrentDialogStyle).show();
+ }
+
+ public String getSelectedUsers() {
+ String str = "";
+ for (DBUser us : users) {
+ if (!us.isAddFlag) {
+ str = str + us.phone + ",";
+ }
+ }
+ return str;
+ }
+
+ public void createGroup(View view) {
+ String userStr = getSelectedUsers();
+ if (userStr == null || userStr == "") {
+ UIUtl.toastI("请添加要加入会议的分机号");
+ return;
+ }
+ //TODO:
+ }
+
+ public void exit(View view) {
+ finish();
+ }
+
+ @Override
+ public boolean handleMessage(@NonNull Message msg) {
+ return super.handleMessage(msg);
+ }
+
+}
diff --git a/app/src/main/java/com/tianrun/sipcall/call/CreateMeetingActivity.java b/app/src/main/java/com/tianrun/sipcall/call/CreateMeetingActivity.java
new file mode 100644
index 0000000..e7a0900
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/call/CreateMeetingActivity.java
@@ -0,0 +1,295 @@
+package com.tianrun.sipcall.call;
+
+import android.content.DialogInterface;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.os.Message;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.GridView;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.alibaba.fastjson.JSONObject;
+import com.qmuiteam.qmui.skin.QMUISkinManager;
+import com.qmuiteam.qmui.widget.dialog.QMUIDialog;
+import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction;
+import com.qmuiteam.qmui.widget.dialog.QMUITipDialog;
+import com.tianrun.sipcall.R;
+import com.tianrun.sipcall.SipEngine;
+import com.tianrun.sipcall.db.DBUser;
+import com.tianrun.sipcall.net.Net;
+import com.tianrun.sipcall.net.NetPkg;
+import com.tianrun.sipcall.ui.TrAdapter;
+import com.tianrun.sipcall.ui.TrBaseActivity;
+import com.tianrun.sipcall.ui.UIUtl;
+import com.tianrun.sipcall.utils.HttpUtl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class CreateMeetingActivity extends TrBaseActivity {
+ TextView meetingTopic;
+ TextView meetingDesc;
+ TextView meetingUsers;
+ GridView gridUsers;
+ TrAdapter adapterUser;
+
+ List users = new ArrayList<>();
+ HashMap mapUsers = new HashMap<>();
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.creatmeeting_view);
+
+ meetingTopic = findViewById(R.id.meeting_name);
+ meetingDesc = findViewById(R.id.meeting_desc);
+ meetingUsers = findViewById(R.id.textViewMembers);
+ gridUsers = findViewById(R.id.GridUser_select);
+
+ users.clear();
+ mapUsers.clear();
+ DBUser u = new DBUser("", "添加分机号", "");
+ u.isAddFlag = true;
+ users.add(u);
+
+ setList(null);
+ refreshSelectedUsers();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ refreshSelectedUsers();
+ }
+
+ public class UserSelectViews {
+ public TextView textViewName;
+ public TextView textViewNum;
+ public ImageView imageViewStatus;
+ public Button buttonDel;
+ public CheckBox checkBox;
+ public ImageView getImageViewPhoneIcon;
+ public ImageView imageViewAddflag;
+ }
+
+ public void setList(List list) {
+ if (list != null) {
+ for (DBUser u : list) {
+ mapUsers.put(u.phone, u);
+ users.add(users.size() - 1, u);
+ }
+ }
+
+ if (adapterUser == null) {
+ adapterUser = UIUtl.setList(this, gridUsers, R.layout.list_item_user_select, users, new TrAdapter.Callback() {
+ @Override
+ public void initCallback(Object data, int position, View prefabView, ViewGroup parent) {
+ UserSelectViews views;
+ if (prefabView.getTag() == null) {
+ views = new UserSelectViews();
+ views.textViewName = prefabView.findViewById(R.id.textViewName);
+ views.textViewNum = prefabView.findViewById(R.id.textViewNum);
+ views.imageViewStatus = prefabView.findViewById(R.id.imageViewStatus);
+ views.buttonDel = prefabView.findViewById(R.id.buttonDel);
+ views.checkBox = prefabView.findViewById(R.id.checkBox);
+ views.getImageViewPhoneIcon = prefabView.findViewById(R.id.imageView3PhoneIcon);
+ views.imageViewAddflag = prefabView.findViewById(R.id.imageViewAddFlag);
+ prefabView.setTag(views);
+ } else {
+ views = (UserSelectViews) (prefabView.getTag());
+ }
+
+ DBUser user = (DBUser) data;
+
+ if (user.isAddFlag) {
+ views.imageViewAddflag.setVisibility(View.VISIBLE);
+ views.buttonDel.setVisibility(View.INVISIBLE);
+ views.checkBox.setVisibility(View.INVISIBLE);
+ views.getImageViewPhoneIcon.setVisibility(View.INVISIBLE);
+ } else {
+ views.imageViewAddflag.setVisibility(View.INVISIBLE);
+ views.buttonDel.setVisibility(View.VISIBLE);
+ views.checkBox.setVisibility(View.INVISIBLE);
+ views.getImageViewPhoneIcon.setVisibility(View.VISIBLE);
+ }
+ views.textViewName.setText(user.name);
+ views.textViewNum.setText(user.phone);
+ if (user.isOffline()) {
+ views.imageViewStatus.setColorFilter(Color.GRAY);
+ } else if (user.isOnline()) {
+ views.imageViewStatus.setColorFilter(Color.GREEN);
+ } else if (user.isBusy()) {
+ views.imageViewStatus.setColorFilter(Color.RED);
+ } else {
+ views.imageViewStatus.setColorFilter(Color.GRAY);
+ }
+ views.checkBox.setChecked(true);
+ views.checkBox.setClickable(false);
+// views.checkBox.setOnClickListener(new View.OnClickListener() {
+// @Override
+// public void onClick(View v) {
+// UserSelect d = (UserSelect) data;
+// d.isSelected = !d.isSelected;
+// adapterUser.notifyDataSetChanged();
+// refreshSelectedUsers();
+// }
+// });
+ views.buttonDel.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ users.remove(data);
+ mapUsers.remove(user.phone);
+ adapterUser.notifyDataSetChanged();
+ refreshSelectedUsers();
+ }
+ });
+ }
+
+ @Override
+ public void clickCallback(Object data, AdapterView> parent, View view, int position, long id) {
+ DBUser d = (DBUser) data;
+ if (d.isAddFlag) {
+ showUsersMultiChoiceDialog();
+ }
+ }
+ });
+ } else {
+ adapterUser.notifyDataSetChanged();
+ refreshSelectedUsers();
+ }
+ }
+
+ private void showUsersMultiChoiceDialog() {
+ List list = new ArrayList<>();
+ List listPhone = new ArrayList<>();
+ for (DBUser u : DBUser.allUser) {
+ if (mapUsers.get(u.phone) == null) {
+ listPhone.add(u.phone);
+ list.add(u.phone + " | " + u.name);
+ }
+ }
+ if(list.size() == 0) {
+ UIUtl.toastI("所有分机已经添加");
+ return;
+ }
+
+ final String[] items = new String[list.size()];
+ list.toArray(items);
+ final QMUIDialog.MultiCheckableDialogBuilder builder = new QMUIDialog.MultiCheckableDialogBuilder(this)
+ .setTitle("选择要加入的分机号")
+// .setCheckedItems(new int[]{1, 3})
+ .setSkinManager(QMUISkinManager.defaultInstance(this))
+ .addItems(items, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ }
+ });
+ builder.addAction("取消", new QMUIDialogAction.ActionListener() {
+ @Override
+ public void onClick(QMUIDialog dialog, int index) {
+ dialog.dismiss();
+ }
+ });
+ builder.addAction("确定", new QMUIDialogAction.ActionListener() {
+ @Override
+ public void onClick(QMUIDialog dialog, int index) {
+ int[] selectIndexs = builder.getCheckedItemIndexes();
+ if (selectIndexs.length > 0) {
+ List list = new ArrayList<>();
+ for (int i = 0; i < selectIndexs.length; i++) {
+ String phNum = listPhone.get(selectIndexs[i]);
+ list.add(DBUser.getUser(phNum));
+ }
+ setList(list);
+ }
+
+ dialog.dismiss();
+ }
+ });
+
+ int mCurrentDialogStyle = com.qmuiteam.qmui.R.style.QMUI_Dialog;
+ builder.create(mCurrentDialogStyle).show();
+ }
+
+ public void refreshSelectedUsers() {
+ meetingUsers.setText("已选择:" + getSelectedUsers());
+ }
+
+ public String getSelectedUsers() {
+ String str = "";
+ for (DBUser us : users) {
+ if (!us.isAddFlag) {
+ str = str + us.phone + ",";
+ }
+ }
+ return str;
+ }
+
+ public void createMeetingVoice(View view) {
+ createMeeting(false);
+
+ }
+
+ public void createMeetingVideo(View view) {
+ createMeeting(true);
+ }
+
+ void createMeeting(boolean isVideo) {
+ String userStr = getSelectedUsers();
+ if (userStr == null || userStr == "") {
+ UIUtl.toastI("请添加要加入会议的分机号");
+ return;
+ }
+ QMUITipDialog dialog = UIUtl.toastLoading("");
+ Net.createMeeting(meetingTopic.getText().toString(), meetingDesc.getText().toString(), userStr, new HttpUtl.CallBack() {
+ @Override
+ public void onRequestComplete(int cmd, String result, Object orgs) {
+ JSONObject jo = JSONObject.parseObject(result);
+ if (jo != null) {
+ String code = "";
+ if(isVideo) {
+ code = jo.getString("video_code");
+ } else {
+ code = jo.getString("audio_code");
+ }
+ SipEngine.getInstance().CallNumber(code, isVideo);
+ }
+ dialog.dismiss();
+ finish();
+ }
+
+ @Override
+ public void onRequestError(int cmd, String result, Object orgs) {
+ dialog.dismiss();
+ }
+ }, isVideo);
+ }
+
+ public void exit(View view) {
+ finish();
+ }
+
+ @Override
+ public boolean handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case Net.CMD_createMeeting:
+ NetPkg pkg =(NetPkg)(msg.obj);
+
+ break;
+ }
+ return super.handleMessage(msg);
+ }
+
+}
diff --git a/app/src/main/java/com/tianrun/sipcall/call/InCallActivity.java b/app/src/main/java/com/tianrun/sipcall/call/InCallActivity.java
new file mode 100644
index 0000000..059fbfa
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/call/InCallActivity.java
@@ -0,0 +1,277 @@
+package com.tianrun.sipcall.call;
+
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.TextView;
+
+import com.tianrun.sipcall.R;
+import com.tianrun.sipcall.SipEngine;
+import com.tianrun.sipcall.ui.TrBaseActivity;
+import com.tianrun.sipcall.utils.CONS;
+import com.tianrun.sipcall.utils.logmy;
+
+import blue.view.EngineServer;
+import blue.view.SMPercentFrameLayout;
+import blue.view.SMSurfaceViewRenderer;
+
+
+public class InCallActivity extends TrBaseActivity implements OnClickListener {
+ public static Handler handler_CallActivity;
+ public static String TAG = "CallActivity";
+ private Handler handler = new Handler(this);
+ private PowerManager powerManager = null;
+ private WakeLock wakeLock = null;
+ private SMSurfaceViewRenderer localRender;
+ private SMSurfaceViewRenderer remoteRender;
+ private SMPercentFrameLayout localRenderLayout;
+ private SMPercentFrameLayout remoteRenderLayout;
+ private ImageButton incall_answer, incall_hangup;
+ private TextView show;
+// private Button meetingbutton;
+
+ private EngineServer engineServer;
+
+ private int callid = -1;//当前通话的id
+ private String callnumber = "未知";
+ private String callstate = "未知";
+ private int calltype = 0; //0音频1视频
+ private boolean VIDEOSTATE = false;
+ public static Intent incallIntent;
+
+ @SuppressLint("InvalidWakeLockTag")
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+// fullScreen();
+ setContentView(R.layout.incallactivity);
+ handler_CallActivity = handler;
+ powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ //获取PowerManager.WakeLock对象,后面的参数|表示同时传入两个值,最后的是调试用的Tag
+ wakeLock = powerManager.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "bright");
+ //点亮屏幕
+ wakeLock.acquire();
+
+ KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+ KeyguardManager.KeyguardLock kl = km.newKeyguardLock("unLock");
+ kl.disableKeyguard();
+ initview();
+ incallIntent = getIntent();
+ setDate(this.getIntent());
+ }
+
+ private void initview() {
+ show = (TextView) findViewById(R.id.show);
+ incall_answer = (ImageButton) findViewById(R.id.incall_answer);
+ incall_hangup = (ImageButton) findViewById(R.id.incall_hangup);
+ incall_hangup.setOnClickListener(this);
+ incall_answer.setOnClickListener(this);
+ //下面为视频部分
+ localRender = (SMSurfaceViewRenderer) findViewById(R.id.local_video_view);
+ remoteRender = (SMSurfaceViewRenderer) findViewById(R.id.remote_video_view);
+ localRenderLayout = (SMPercentFrameLayout) findViewById(R.id.local_video_layout);
+ remoteRenderLayout = (SMPercentFrameLayout) findViewById(R.id.remote_video_layout);
+ engineServer = new EngineServer(localRender, remoteRender, localRenderLayout, remoteRenderLayout, true);
+
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ fullScreen();
+ incallIntent = getIntent();
+ setDate(intent);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (callstate.equals("来电")) {
+ incall_answer.setVisibility(View.VISIBLE);
+ } else {
+ incall_answer.setVisibility(View.GONE);
+ }
+ show.setText(callnumber + callstate);
+ if (incall_answer.getVisibility() == View.VISIBLE) {
+ incall_answer.setVisibility(View.GONE);
+ SipEngine.getInstance().answer(callid);
+ }
+
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+// onDestroy();
+ }
+
+ @Override
+ protected void onDestroy() {
+ stopVideoStream(true);
+ super.onDestroy();
+ SipEngine.getInstance().hangup(callid);
+ handler_CallActivity = null;
+ if (wakeLock != null) {
+ wakeLock.release();
+ wakeLock = null;
+ }
+ }
+
+ /**
+ * 变更通话数据
+ */
+ public void setDate(Intent intent) {
+ Bundle bundle = intent.getExtras();
+ callid = bundle.getInt("callid");
+ callnumber = bundle.getString("callnumber");
+ callstate = bundle.getString("callstate");
+ calltype = bundle.getInt("calltype");
+ }
+
+ /**
+ * 停止视频
+ *
+ * @param isExit 是否完全退出视频
+ */
+ public synchronized void stopVideoStream(boolean isExit) {
+ if (VIDEOSTATE && engineServer != null) {
+ engineServer.stopVideoStream(isExit);
+ VIDEOSTATE = false;
+ }
+ }
+
+ @Override
+ public boolean handleMessage(Message m) {
+ switch (m.what) {
+ case CONS.CALLSTATE:
+ callstate = (String) m.obj;
+ show.setText(callnumber + callstate);
+ if (callstate.equals("挂断")) {
+ stopVideoStream(true);
+ finish();
+ }
+ break;
+ case CONS.MEDIASTATE:
+ CallMediaUtils utils = (CallMediaUtils) m.obj;
+ int r = utils.getR();
+ int l = utils.getL();
+ int payload = utils.getPayload();
+ if (r != 0 || l != 0) {
+ //开始视频
+ ShowVideoView(true);
+ startVideoStream(SipEngine.getInstance().getip(), r, l, payload);
+ } else {
+ //开始音频
+ stopVideoStream(false);
+ ShowVideoView(false);
+ }
+ break;
+ case CONS.CALLDOWN:
+ stopVideoStream(true);
+ finish();
+ break;
+ }
+ return true;
+ }
+
+ private synchronized void startVideoStream(String server, int rport, int lport, final int payload) {
+ int BitRate = 768;
+ int FrameRate = 10;
+ int w = 480;
+ int h = 270;
+ String camerafb = "Front";
+ int c = 1;
+ switch (camerafb) {
+ case "Front":
+ c = 1;
+ break;
+ case "Post":
+ c = 0;
+ break;
+ case "Usb":
+ c = 2;
+ break;
+ }
+ c = 0;
+ logmy.e(callnumber + "--" + server + "--" + rport + "--" + lport);
+ if (engineServer != null) {
+ ShowVideoView(true);
+ /*
+ * 开始视频
+ * @param context
+ * @param server 服务器地址
+ * @param rport 远程端口
+ * @param lport 本地端口
+ * @param payload payload
+ * @param BitRate 带宽
+ * @param FrameRate 帧率
+ * @param w 宽
+ * @param h 高
+ * @param camtype 摄像头前后外置
+ * @param videoHwAcceleration 是否启用硬编
+ * @param recordTx 是否录制远程画面
+ * @param recordRx 是否录制本地画面
+ * @param saveVideoDir 录制画面存放的文件
+ */
+ engineServer.startVideoStream(this, server, rport, lport, payload, BitRate, FrameRate, w, h, c, true);
+ VIDEOSTATE = true;
+ }
+
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.incall_hangup:
+ SipEngine.getInstance().hangup(callid);
+ if (SipEngine.callPagesConfig.size() == 0) {
+ finish();
+ }
+ break;
+ case R.id.incall_answer:
+ incall_answer.setVisibility(View.GONE);
+ SipEngine.getInstance().answer(callid);
+ break;
+ }
+ }
+
+
+ public void ShowVideoView(boolean show) {
+ if (show) {
+ localRenderLayout.setVisibility(View.VISIBLE);
+ remoteRenderLayout.setVisibility(View.VISIBLE);
+ } else {
+ localRenderLayout.setVisibility(View.GONE);
+ remoteRenderLayout.setVisibility(View.GONE);
+ }
+ }
+
+ @SuppressLint("NewApi")
+ void fullScreen() {
+ int flag = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ getWindow().getDecorView().setSystemUiVisibility(flag);
+ }
+
+ public void switchCamera() {
+ if (engineServer != null) {
+ engineServer.switchCamera(null);
+ }
+ }
+}
diff --git a/app/src/main/java/com/tianrun/sipcall/call/InCallMeetingActivity.java b/app/src/main/java/com/tianrun/sipcall/call/InCallMeetingActivity.java
new file mode 100644
index 0000000..43c4f9e
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/call/InCallMeetingActivity.java
@@ -0,0 +1,276 @@
+package com.tianrun.sipcall.call;
+
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageButton;
+import android.widget.TextView;
+
+import com.tianrun.sipcall.R;
+import com.tianrun.sipcall.SipEngine;
+import com.tianrun.sipcall.ui.TrBaseActivity;
+import com.tianrun.sipcall.utils.CONS;
+import com.tianrun.sipcall.utils.logmy;
+
+import blue.view.EngineServer;
+import blue.view.SMPercentFrameLayout;
+import blue.view.SMSurfaceViewRenderer;
+
+
+public class InCallMeetingActivity extends TrBaseActivity implements OnClickListener {
+ public static Handler handler_CallActivity;
+ public static String TAG = "CallMeetingActivity";
+ private Handler handler = new Handler(this);
+ private PowerManager powerManager = null;
+ private WakeLock wakeLock = null;
+ private SMSurfaceViewRenderer localRender;
+ private SMSurfaceViewRenderer remoteRender;
+ private SMPercentFrameLayout localRenderLayout;
+ private SMPercentFrameLayout remoteRenderLayout;
+ private ImageButton incall_answer, incall_hangup;
+ private TextView show;
+
+ private EngineServer engineServer;
+
+ private int callid = -1;//当前通话的id
+ private String callnumber = "未知";
+ private String callstate = "未知";
+ private int calltype = 0; //0音频1视频
+ private boolean VIDEOSTATE = false;
+ public static Intent incallIntent;
+
+ @SuppressLint("InvalidWakeLockTag")
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+// fullScreen();
+ setContentView(R.layout.incallmeetingactivity);
+ handler_CallActivity = handler;
+ powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ //获取PowerManager.WakeLock对象,后面的参数|表示同时传入两个值,最后的是调试用的Tag
+ wakeLock = powerManager.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "bright");
+ //点亮屏幕
+ wakeLock.acquire();
+
+ KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+ KeyguardManager.KeyguardLock kl = km.newKeyguardLock("unLock");
+ kl.disableKeyguard();
+ initview();
+ incallIntent = getIntent();
+ setDate(this.getIntent());
+ }
+
+ private void initview() {
+ show = (TextView) findViewById(R.id.show);
+ incall_answer = (ImageButton) findViewById(R.id.incall_answer);
+ incall_hangup = (ImageButton) findViewById(R.id.incall_hangup);
+ incall_hangup.setOnClickListener(this);
+ incall_answer.setOnClickListener(this);
+ //下面为视频部分
+ localRender = (SMSurfaceViewRenderer) findViewById(R.id.local_video_view);
+ remoteRender = (SMSurfaceViewRenderer) findViewById(R.id.remote_video_view);
+ localRenderLayout = (SMPercentFrameLayout) findViewById(R.id.local_video_layout);
+ remoteRenderLayout = (SMPercentFrameLayout) findViewById(R.id.remote_video_layout);
+ engineServer = new EngineServer(localRender, remoteRender, localRenderLayout, remoteRenderLayout, true);
+
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ fullScreen();
+ incallIntent = getIntent();
+ setDate(intent);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (callstate.equals("来电")) {
+ incall_answer.setVisibility(View.VISIBLE);
+ } else {
+ incall_answer.setVisibility(View.GONE);
+ }
+ show.setText(callnumber + callstate);
+ //控件显示号码,来电,去电,通话中...
+ if (incall_answer.getVisibility() == View.VISIBLE) {
+ incall_answer.setVisibility(View.GONE);
+ SipEngine.getInstance().answer(callid);
+ }
+
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+// onDestroy();
+ }
+
+ @Override
+ protected void onDestroy() {
+ stopVideoStream(true);
+ super.onDestroy();
+ SipEngine.getInstance().hangup(callid);
+ handler_CallActivity = null;
+ if (wakeLock != null) {
+ wakeLock.release();
+ wakeLock = null;
+ }
+ }
+
+ /**
+ * 变更通话数据
+ */
+ public void setDate(Intent intent) {
+ Bundle bundle = intent.getExtras();
+ callid = bundle.getInt("callid");
+ callnumber = bundle.getString("callnumber");
+ callstate = bundle.getString("callstate");
+ calltype = bundle.getInt("calltype");
+ }
+
+ /**
+ * 停止视频
+ *
+ * @param isExit 是否完全退出视频
+ */
+ public synchronized void stopVideoStream(boolean isExit) {
+ if (VIDEOSTATE && engineServer != null) {
+ engineServer.stopVideoStream(isExit);
+ VIDEOSTATE = false;
+ }
+ }
+
+ @Override
+ public boolean handleMessage(Message m) {
+ switch (m.what) {
+ case CONS.CALLSTATE:
+ callstate = (String) m.obj;
+ show.setText(callnumber + callstate);
+ if (callstate.equals("挂断")) {
+ stopVideoStream(true);
+ finish();
+ }
+ break;
+ case CONS.MEDIASTATE:
+ CallMediaUtils utils = (CallMediaUtils) m.obj;
+ int r = utils.getR();
+ int l = utils.getL();
+ int payload = utils.getPayload();
+ if (r != 0 || l != 0) {
+ //开始视频
+ ShowVideoView(true);
+ startVideoStream(SipEngine.getInstance().getip(), r, l, payload);
+ } else {
+ //开始音频
+ stopVideoStream(false);
+ ShowVideoView(false);
+ }
+ break;
+ case CONS.CALLDOWN:
+ stopVideoStream(true);
+ finish();
+ break;
+ }
+ return true;
+ }
+
+ private synchronized void startVideoStream(String server, int rport, int lport, final int payload) {
+ int BitRate = 768;
+ int FrameRate = 10;
+ int w = 480;
+ int h = 270;
+ String camerafb = "Front";
+ int c = 1;
+ switch (camerafb) {
+ case "Front":
+ c = 1;
+ break;
+ case "Post":
+ c = 0;
+ break;
+ case "Usb":
+ c = 2;
+ break;
+ }
+ c = 0;
+ logmy.e(callnumber + "--" + server + "--" + rport + "--" + lport);
+ if (engineServer != null) {
+ ShowVideoView(true);
+ /*
+ * 开始视频
+ * @param context
+ * @param server 服务器地址
+ * @param rport 远程端口
+ * @param lport 本地端口
+ * @param payload payload
+ * @param BitRate 带宽
+ * @param FrameRate 帧率
+ * @param w 宽
+ * @param h 高
+ * @param camtype 摄像头前后外置
+ * @param videoHwAcceleration 是否启用硬编
+ * @param recordTx 是否录制远程画面
+ * @param recordRx 是否录制本地画面
+ * @param saveVideoDir 录制画面存放的文件
+ */
+ engineServer.startVideoStream(this, server, rport, lport, payload, BitRate, FrameRate, w, h, c, true);
+ VIDEOSTATE = true;
+ }
+
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.incall_hangup:
+ SipEngine.getInstance().hangup(callid);
+ if (SipEngine.callPagesConfig.size() == 0) {
+ finish();
+ }
+ break;
+ case R.id.incall_answer:
+ incall_answer.setVisibility(View.GONE);
+ SipEngine.getInstance().answer(callid);
+ break;
+ }
+ }
+
+
+ public void ShowVideoView(boolean show) {
+ if (show) {
+ localRenderLayout.setVisibility(View.VISIBLE);
+ remoteRenderLayout.setVisibility(View.VISIBLE);
+ } else {
+ localRenderLayout.setVisibility(View.GONE);
+ remoteRenderLayout.setVisibility(View.GONE);
+ }
+ }
+
+ @SuppressLint("NewApi")
+ void fullScreen() {
+ int flag = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ getWindow().getDecorView().setSystemUiVisibility(flag);
+ }
+
+ public void switchCamera() {
+ if (engineServer != null) {
+ engineServer.switchCamera(null);
+ }
+ }
+}
diff --git a/app/src/main/java/com/tianrun/sipcall/call/MyEngineServer.java b/app/src/main/java/com/tianrun/sipcall/call/MyEngineServer.java
new file mode 100644
index 0000000..f3c3b7a
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/call/MyEngineServer.java
@@ -0,0 +1,221 @@
+package com.tianrun.sipcall.call;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.bluetel.media.MediaConstraintsAndScalingType;
+import com.bluetel.media.SMCodec;
+import com.bluetel.media.SMConnection;
+import com.bluetel.media.SMMediaEngine;
+import com.bluetel.media.SMMediaStream;
+import com.bluetel.media.SMVideoCapturer;
+import com.bluetel.media.SMVideoRenderer;
+import com.bluetel.media.SMVideoSource;
+import com.bluetel.media.SMVideoTrack;
+
+import org.webrtc.Camera1Enumerator;
+import org.webrtc.CameraVideoCapturer;
+import org.webrtc.EglBase;
+import org.webrtc.RendererCommon;
+import org.webrtc.VideoCapturer;
+
+import blue.view.EngineServer;
+import blue.view.SMPercentFrameLayout;
+import blue.view.SMSurfaceViewRenderer;
+
+public class MyEngineServer {
+ public static final String TAG = "EngineServer";
+ private EglBase rootEglBase;
+ private SMConnection connection;
+ private SMMediaEngine engine;
+ private SMVideoCapturer videoCapturer;
+ private SMVideoSource videoSource;
+ private SMMediaStream mediaStream;
+ private SMSurfaceViewRenderer localRender;
+ private SMSurfaceViewRenderer remoteRender;
+ private SMPercentFrameLayout localRenderLayout;
+ private SMPercentFrameLayout remoteRenderLayout;
+ public static final String VIDEO_TRACK_ID = "ARDAMSv0";
+ public static final String VIDEO_TRACK_ID2 = "ARDAMSv1";
+ public static final String AUDIO_TRACK_ID = "ARDAMSa0";
+ public static final String MAX_VIDEO_WIDTH_CONSTRAINT = "maxWidth";
+ public static final String MIN_VIDEO_WIDTH_CONSTRAINT = "minWidth";
+ public static final String MAX_VIDEO_HEIGHT_CONSTRAINT = "maxHeight";
+ public static final String MIN_VIDEO_HEIGHT_CONSTRAINT = "minHeight";
+ public static final String MAX_VIDEO_FPS_CONSTRAINT = "maxFrameRate";
+ public static final String MIN_VIDEO_FPS_CONSTRAINT = "minFrameRate";
+ public static final int MAX_VIDEO_WIDTH = 1920;
+ public static final int MAX_VIDEO_HEIGHT = 1080;
+ public static final int MAX_VIDEO_FPS = 30;
+ private static final int LOCAL_X_CONNECTED = 72;
+ private static final int LOCAL_Y_CONNECTED = 72;
+ private static final int LOCAL_WIDTH_CONNECTED = 25;
+ private static final int LOCAL_HEIGHT_CONNECTED = 25;
+ private static final int REMOTE_X = 0;
+ private static final int REMOTE_Y = 0;
+ private static final int REMOTE_WIDTH = 100;
+ private static final int REMOTE_HEIGHT = 100;
+ private static final String LAYOUT_AVERAGE = "LAYOUT_AVERAGE";
+ private static final String LAYOUT_REMOTEFULL = "LAYOUT_REMOTEFULL";
+ private static final String LAYOUT_LOCALFULL = "LAYOUT_LOCALFULL";
+ private static final String LAYOUT_PRIMARY = "LAYOUT_PRIMARY";
+ private String mLayoutState = "LAYOUT_PRIMARY";
+
+ public MyEngineServer(SMSurfaceViewRenderer var1, SMSurfaceViewRenderer var2, SMPercentFrameLayout var3, SMPercentFrameLayout var4, boolean var5) {
+ this.localRender = var1;
+ this.remoteRender = var2;
+ this.localRenderLayout = var3;
+ this.remoteRenderLayout = var4;
+ this.rootEglBase = EglBase.create();
+ if (var5) {
+ var1.init(this.rootEglBase.getEglBaseContext(), (RendererCommon.RendererEvents)null);
+ }
+
+ var2.init(this.rootEglBase.getEglBaseContext(), (RendererCommon.RendererEvents)null);
+ var1.setZOrderMediaOverlay(true);
+ this.updateVideoView();
+ }
+
+ private void updateVideoView() {
+ this.remoteRenderLayout.setPosition(0, 0, 100, 100);
+ this.remoteRender.setScalingType(MediaConstraintsAndScalingType.SCALE_ASPECT_FIT());
+ this.remoteRender.setMirror(false);
+ this.localRenderLayout.setPosition(72, 72, 25, 25);
+ this.localRender.setScalingType(MediaConstraintsAndScalingType.SCALE_ASPECT_FIT());
+ if (this.mLayoutState.equals("LAYOUT_PRIMARY")) {
+ this.localRenderLayout.setPosition(72, 72, 25, 25);
+ this.localRender.setScalingType(MediaConstraintsAndScalingType.SCALE_ASPECT_FIT());
+ } else if (this.mLayoutState.equals("LAYOUT_AVERAGE")) {
+ this.localRenderLayout.setPosition(50, 0, 50, 100);
+ this.localRender.setScalingType(MediaConstraintsAndScalingType.SCALE_ASPECT_FIT());
+ this.remoteRenderLayout.setPosition(0, 0, 50, 100);
+ this.remoteRender.setScalingType(MediaConstraintsAndScalingType.SCALE_ASPECT_FIT());
+ } else if (this.mLayoutState.equals("LAYOUT_REMOTEFULL")) {
+ this.localRenderLayout.setPosition(100, 100, 0, 0);
+ this.localRender.setScalingType(MediaConstraintsAndScalingType.SCALE_ASPECT_FIT());
+ this.remoteRenderLayout.setPosition(0, 0, 100, 100);
+ this.remoteRender.setScalingType(MediaConstraintsAndScalingType.SCALE_ASPECT_FIT());
+ } else if (this.mLayoutState.equals("LAYOUT_LOCALFULL")) {
+ this.localRenderLayout.setPosition(0, 0, 100, 100);
+ this.localRender.setScalingType(MediaConstraintsAndScalingType.SCALE_ASPECT_FIT());
+ this.remoteRenderLayout.setPosition(100, 100, 0, 0);
+ this.remoteRender.setScalingType(MediaConstraintsAndScalingType.SCALE_ASPECT_FIT());
+ }
+
+ this.localRender.setMirror(false);
+ this.remoteRender.requestLayout();
+ this.localRender.requestLayout();
+ }
+
+ public synchronized void startVideoStream(Context var1, String var2, int var3, int var4, final int var5, int var6, int var7, int var8, int var9, int var10, boolean var11) {
+ SMMediaEngine.initializeGlobals(var1, true, true, var11);
+ this.engine = SMMediaEngine.create();
+ this.connection = this.engine.createConnection(new SMConnection.ConnectionObserver() {
+ public void onAddStream(SMMediaStream var1) {
+ if (var1.videoTracks.size() == 1) {
+ SMVideoTrack var2 = (SMVideoTrack)var1.videoTracks.get(0);
+ var2.setEnabled(true);
+ var2.addRenderer(new SMVideoRenderer(MyEngineServer.this.remoteRender));
+ }
+
+ }
+
+ public void onRemoveStream(SMMediaStream var1) {
+ }
+
+ public SMCodec onReceiveVideoTrack(int var1, int var2) {
+ return new SMCodec(var5, 1);
+ }
+ });
+ this.connection.bindLocalAddr((String)null, var4, var4 + 1);
+ this.connection.addRemoteAddr(var2, var3, var3 + 1);
+ String var12 = Camera1Enumerator.getDeviceName(var10);
+ Log.d("EngineServer", "Opening camera: " + var12);
+ this.videoCapturer = SMVideoCapturer.create(var12, (CameraVideoCapturer.CameraEventsHandler)null, false);
+ if (this.videoCapturer == null) {
+ Log.d("EngineServer", "Failed to open camera");
+ } else {
+ this.videoSource = this.createVideoSource(this.videoCapturer, 1280, 720);
+ this.videoSource.adaptOutputFormat(var8, var9, var7);
+ SMVideoTrack var13 = this.engine.createVideoTrack("ARDAMSv0", this.videoSource);
+ var13.setEnabled(true);
+ SMVideoRenderer var14 = new SMVideoRenderer(this.localRender);
+ var13.addRenderer(var14);
+ this.mediaStream = this.engine.createLocalMediaStream("ARDAMS");
+ this.mediaStream.addTrack(var13);
+ int var15 = var6 * 1000;
+ var15 = (int)((float)var15 * 0.9F);
+ int var16 = var15 / 2;
+ int var17 = (int)((double)var15 * 0.75D);
+ this.mediaStream.setVideoCodec(new SMCodec(var5, 1, 20, var16, var15, var17, false));
+ this.connection.addSendStream(this.mediaStream);
+ }
+ }
+
+ public synchronized void stopVideoStream(boolean var1) {
+ if (this.connection != null) {
+ this.connection.dispose();
+ this.connection = null;
+ }
+
+ if (this.videoCapturer != null) {
+ this.videoCapturer.stopCapture();
+ this.videoCapturer.dispose();
+ this.videoCapturer = null;
+ }
+
+ if (this.videoSource != null) {
+ this.videoSource.dispose();
+ }
+
+ if (this.rootEglBase != null && var1) {
+ this.rootEglBase.release();
+ this.rootEglBase = null;
+ }
+
+ if (this.remoteRender != null && var1) {
+ this.remoteRender.release();
+ this.remoteRender = null;
+ }
+
+ if (this.localRender != null && var1) {
+ this.localRender.release();
+ this.localRender = null;
+ }
+
+ if (this.engine != null && var1) {
+ this.engine.dispose();
+ this.engine = null;
+ }
+
+ }
+
+ private SMVideoSource createVideoSource(VideoCapturer var1, int var2, int var3) {
+ return (new MediaConstraintsAndScalingType(this.engine, var1, var2, var3)).createVideoSource();
+ }
+
+ public synchronized void adjustVideoView(int var1) {
+ if (var1 == 1) {
+ this.mLayoutState = "LAYOUT_AVERAGE";
+ } else if (var1 == 2) {
+ this.mLayoutState = "LAYOUT_REMOTEFULL";
+ } else if (var1 == 3) {
+ this.mLayoutState = "LAYOUT_LOCALFULL";
+ } else {
+ this.mLayoutState = "LAYOUT_PRIMARY";
+ }
+
+ this.updateVideoView();
+ }
+
+ public synchronized void setLayoutState(String var1) {
+ this.mLayoutState = var1;
+ }
+
+ public synchronized void switchCamera(CameraVideoCapturer.CameraSwitchHandler var1) {
+ if (this.videoCapturer != null) {
+ this.videoCapturer.switchCamera(var1);
+ }
+
+ }
+}
diff --git a/app/src/main/java/com/tianrun/sipcall/call/utils/InCallUtils.java b/app/src/main/java/com/tianrun/sipcall/call/utils/InCallUtils.java
new file mode 100644
index 0000000..2a3115a
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/call/utils/InCallUtils.java
@@ -0,0 +1,103 @@
+package com.tianrun.sipcall.call.utils;
+
+public class InCallUtils {
+
+ private String callNumber;
+ private boolean isHolder;
+ private boolean isVideo;
+ private int rPort;
+ private int lPort;
+ private String timeThreadId;
+ private String callState;
+ private boolean isCurrentCall;
+ private int payload;
+ private int callId;
+
+
+ public InCallUtils(String callNumber, boolean isHolder, boolean isVideo,
+ int rPort, int lPort, String timeThreadId, String callState,
+ boolean isCurrentCall, int payload,int callId) {
+ super();
+ this.callNumber = callNumber;
+ this.isHolder = isHolder;
+ this.isVideo = isVideo;
+ this.rPort = rPort;
+ this.lPort = lPort;
+ this.timeThreadId = timeThreadId;
+ this.callState = callState;
+ this.isCurrentCall = isCurrentCall;
+ this.payload = payload;
+ this.callId = callId;
+ }
+
+
+ public int getCallId() {
+ return callId;
+ }
+
+
+ public void setCallId(int callId) {
+ this.callId = callId;
+ }
+
+
+ public int getPayload() {
+ return payload;
+ }
+ public void setPayload(int payload) {
+ this.payload = payload;
+ }
+ public boolean isCurrentCall() {
+ return isCurrentCall;
+ }
+ public void setCurrentCall(boolean isCurrentCall) {
+ this.isCurrentCall = isCurrentCall;
+ }
+ public String getCallState() {
+ return callState;
+ }
+ public void setCallState(String callState) {
+ this.callState = callState;
+ }
+ public String getCallNumber() {
+ return callNumber;
+ }
+ public void setCallNumber(String callNumber) {
+ this.callNumber = callNumber;
+ }
+ public boolean isHolder() {
+ return isHolder;
+ }
+ public void setHolder(boolean isHolder) {
+ this.isHolder = isHolder;
+ }
+ public boolean isVideo() {
+ return isVideo;
+ }
+ public void setVideo(boolean isVideo) {
+ this.isVideo = isVideo;
+ }
+ public int getrPort() {
+ return rPort;
+ }
+ public void setrPort(int rPort) {
+ this.rPort = rPort;
+ }
+ public int getlPort() {
+ return lPort;
+ }
+ public void setlPort(int lPort) {
+ this.lPort = lPort;
+ }
+ public String getTimeThreadId() {
+ return timeThreadId;
+ }
+ public void setTimeThreadId(String timeThreadId) {
+ this.timeThreadId = timeThreadId;
+ }
+
+
+
+
+
+}
diff --git a/app/src/main/java/com/tianrun/sipcall/db/DBHead.java b/app/src/main/java/com/tianrun/sipcall/db/DBHead.java
new file mode 100644
index 0000000..20329a7
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/db/DBHead.java
@@ -0,0 +1,68 @@
+package com.tianrun.sipcall.db;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DBHead {
+ public String name;
+ public String desc;
+ public String audio_code;
+ public String video_code;
+ public String status;
+ public String id;
+ public boolean isSelected = false;
+
+ public static List allGroups = new ArrayList<>();
+
+ public DBHead(String id, String name) {
+ this.id = id;
+ this.name = name;
+
+ }
+
+ public DBHead(JSONObject o) {
+ name = o.getString("topic");
+ desc = o.getString("description");
+ audio_code = o.getString("audio_code");
+ video_code = o.getString("video_code");
+ status = o.getString("audio_status");
+ id = o.getString("task_uuid");
+ }
+
+ public static DBHead newHead4Total() {
+ DBHead u = new DBHead("-999", "全部");
+ return u;
+ }
+
+ public boolean isAll() {
+ return id.equals("-999");
+ }
+
+ public static void onGetGroups(JSONArray list) {
+ allGroups.clear();
+ for (Object o : list) {
+ allGroups.add(new DBHead((JSONObject) o));
+ }
+ }
+
+ public static DBHead getByUUID(String uuid) {
+ for (DBHead d : allGroups) {
+ if (d.id.equals(uuid)){
+ return d;
+ }
+ }
+ return null;
+ }
+
+ public static DBHead getByCode(String code) {
+ for (DBHead d : allGroups) {
+ if (d.audio_code.equals(code) || d.video_code.equals(code)) {
+ return d;
+ }
+ }
+ return null;
+ }
+}
diff --git a/app/src/main/java/com/tianrun/sipcall/db/DBRoot.java b/app/src/main/java/com/tianrun/sipcall/db/DBRoot.java
new file mode 100644
index 0000000..95abc5e
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/db/DBRoot.java
@@ -0,0 +1,23 @@
+package com.tianrun.sipcall.db;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.tianrun.sipcall.net.Net;
+
+public class DBRoot {
+
+ public static void onGetData(int cmd, String content) {
+ switch (cmd) {
+ case Net.CMD_getUsers:
+ JSONArray allUsers = JSONObject.parseObject(content).getJSONArray("agents");
+ DBUser.onGetUsers(allUsers);
+ break;
+ case Net.CMD_getGroups:
+ JSONArray list = JSONArray.parseArray(content);
+ DBHead.onGetGroups(list);
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/app/src/main/java/com/tianrun/sipcall/db/DBUser.java b/app/src/main/java/com/tianrun/sipcall/db/DBUser.java
new file mode 100644
index 0000000..6868818
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/db/DBUser.java
@@ -0,0 +1,62 @@
+package com.tianrun.sipcall.db;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class DBUser {
+ public static List allUser = new ArrayList<>();
+ public static HashMap mapUsers = new HashMap<>();
+ public static DBUser mySelf;
+ public String name = "";
+ public String phone = "";
+ public String status = "";
+ public boolean isManager = false;
+ public boolean isAddFlag = false;
+
+ public DBUser(String name, String phone, String status) {
+ this.name = name;
+ this.phone = phone;
+ this.status = status;
+ }
+
+ public DBUser(JSONObject d) {
+ this.name = d.getString("name");
+ this.phone = d.getString("extn");
+ this.status = d.getString("sip_state");
+ this.isManager = d.getString("weight").equals("1");
+ }
+
+ public boolean isBusy() {
+ return status.equals("busy");
+ }
+
+ public boolean isOnline() {
+ return status.equals("true");
+ }
+
+ public boolean isOffline() {
+ return status.equals("false");
+ }
+
+ public static void onGetUsers(JSONArray array) {
+ allUser.clear();
+ DBUser u;
+ for (Object o : array) {
+ u = new DBUser((JSONObject) o);
+ allUser.add(u);
+ mapUsers.put(u.phone, u);
+ if (mySelf != null && u.phone == mySelf.phone) {
+ mySelf = u;
+ }
+ }
+ }
+
+ public static DBUser getUser(String phoneNo) {
+ return mapUsers.get(phoneNo);
+ }
+
+}
diff --git a/app/src/main/java/com/tianrun/sipcall/login/LoginActivity.java b/app/src/main/java/com/tianrun/sipcall/login/LoginActivity.java
new file mode 100644
index 0000000..56d488f
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/login/LoginActivity.java
@@ -0,0 +1,359 @@
+package com.tianrun.sipcall.login;
+
+
+import android.Manifest;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+import com.qmuiteam.qmui.widget.dialog.QMUIDialog;
+import com.qmuiteam.qmui.widget.dialog.QMUITipDialog;
+import com.tianrun.sipcall.App;
+import com.tianrun.sipcall.R;
+import com.tianrun.sipcall.SipEngine;
+import com.tianrun.sipcall.call.InCallActivity;
+import com.tianrun.sipcall.call.InCallMeetingActivity;
+import com.tianrun.sipcall.db.DBUser;
+import com.tianrun.sipcall.net.Net;
+import com.tianrun.sipcall.ui.ActivityMgr;
+import com.tianrun.sipcall.ui.TrBaseActivity;
+import com.tianrun.sipcall.ui.UIUtl;
+import com.tianrun.sipcall.utils.CONS;
+import com.tianrun.sipcall.utils.HttpUtl;
+
+import com.alibaba.fastjson.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class LoginActivity extends TrBaseActivity {
+ private TextView show;
+ private EditText username, userpw, userip, userport;
+
+ /**
+ * 账号密码服务器地址端口直接在这输入
+ */
+ private String name = "";
+ private String pw = "";
+ private int port = 0;
+ private String ip = "";
+ QMUITipDialog dialogLoading;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_login);
+ show = findViewById(R.id.show);
+ username = findViewById(R.id.username);
+ userpw = findViewById(R.id.userpw);
+ userip = findViewById(R.id.userip);
+ userport = findViewById(R.id.userport);
+ if (Build.VERSION.SDK_INT >= 23) {//6.0才用动态权限
+ initPermission();
+ }
+
+ String str = getUserInfor();
+ if (str != null && !str.isEmpty()) {
+ JSONObject obj = JSONObject.parseObject(str);
+ username.setText(obj.getString("name"));
+ userpw.setText(obj.getString("password"));
+ userip.setText(obj.getString("ip"));
+ userport.setText(obj.getInteger("port").toString());
+
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ boolean donotAutoLogin = false;
+ Intent intent = getIntent();
+ if (intent != null) {
+ Bundle bundle = intent.getExtras();
+ if(bundle != null) {
+ donotAutoLogin = bundle.getBoolean("donotAutoLogin");
+ }
+ }
+ if (!donotAutoLogin) {
+ doLogin();
+ }
+ }
+ }, 100);
+ }
+
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (InCallActivity.handler_CallActivity != null) {
+ startActivity(InCallActivity.incallIntent);
+ return;
+ }
+ if (InCallMeetingActivity.handler_CallActivity != null) {
+ startActivity(InCallMeetingActivity.incallIntent);
+ return;
+ }
+ if (SipEngine.getInstance().onLine) {
+ startActivity(new Intent(LoginActivity.this, MainActivity.class));
+ finish();
+ return;
+ }
+ }
+
+ public void login(View view) {
+ doLogin();
+ }
+
+ public void doLogin() {
+ if (SipEngine.getInstance().isonLine())
+ return;
+ if (!isEnable())
+ return;
+ dialogLoading = UIUtl.toastLoading("");
+ SipEngine.getInstance().init();//先初始化
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ SipEngine.getInstance().Register(
+ name,
+ pw,
+ ip,
+ port);//再注册
+ }
+ }, 1000);
+ }
+
+ private boolean isEnable() {
+ boolean a = false;
+ boolean b = false;
+ boolean c = false;
+ boolean d = false;
+ if (username.getText().toString() != null && !username.getText().toString().equals("")) {
+ name = username.getText().toString();
+ a = true;
+ }
+ if (userpw.getText().toString() != null && !userpw.getText().toString().equals("")) {
+ pw = userpw.getText().toString();
+ b = true;
+ }
+ if (userip.getText().toString() != null && !userip.getText().toString().equals("")) {
+ ip = userip.getText().toString();
+ c = true;
+ }
+ if (userport.getText().toString() != null && !userport.getText().toString().equals("")) {
+ port = Integer.parseInt(userport.getText().toString());
+ d = true;
+ }
+
+ return a && b && c && d;
+
+ }
+
+ public String getUserInfor() {
+ SharedPreferences sp = getSharedPreferences("UserInfor", MODE_PRIVATE);
+ return sp.getString("content", null);
+ }
+
+ public void saveUserInfor(String name, String password, String ip, int port) {
+ JSONObject jo = new JSONObject();
+ jo.put("name", name);
+ jo.put("password", password);
+ jo.put("ip", ip);
+ jo.put("port", port);
+ SharedPreferences sp = getSharedPreferences("UserInfor", MODE_PRIVATE);
+ sp.edit().putString("content", jo.toJSONString()).commit();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ }
+
+ @Override
+ public boolean handleMessage(Message m) {
+ switch (m.what) {
+ case CONS.LOGINSHOW:
+ String str = (String) m.obj;
+ show.setText(str);
+ break;
+ case CONS.LOGINFAILED:
+ if(dialogLoading != null) {
+ dialogLoading.dismiss();
+ }
+ case CONS.LOGIN:
+ if(dialogLoading != null) {
+ dialogLoading.dismiss();
+ }
+ if (isEnable()) {
+ saveUserInfor(this.name, this.pw, this.ip, this.port);
+ }
+ startActivity(new Intent(this, MainActivity.class));
+ DBUser.mySelf = new DBUser("", this.name, "true");
+ finish();
+ break;
+ }
+ return true;
+ }
+
+
+ /****************************************权限设置***************************************/
+
+// 所需的全部权限
+ public static String[] permissions = new String[]{
+ Manifest.permission.NFC,
+ Manifest.permission.RECORD_AUDIO,
+ Manifest.permission.INTERNET,
+// Manifest.permission.READ_LOGS,
+ Manifest.permission.ACCESS_NETWORK_STATE,
+ Manifest.permission.ACCESS_WIFI_STATE,
+ Manifest.permission.READ_PHONE_STATE,
+// Manifest.permission.WRITE_SETTINGS,
+ Manifest.permission.CAMERA,
+ Manifest.permission.MODIFY_AUDIO_SETTINGS,
+// Manifest.permission.BIND_APPWIDGET,
+// Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS,
+// Manifest.permission.SYSTEM_OVERLAY_WINDOW,
+ Manifest.permission.RESTART_PACKAGES,
+// Manifest.permission.SYSTEM_ALERT_WINDOW,
+// Manifest.permission.SET_TIME_ZONE,
+ Manifest.permission.READ_CALL_LOG,
+ Manifest.permission.BROADCAST_STICKY,
+ Manifest.permission.GET_ACCOUNTS,
+ Manifest.permission.BLUETOOTH,
+ Manifest.permission.CHANGE_WIFI_MULTICAST_STATE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE,
+ Manifest.permission.CHANGE_WIFI_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.VIBRATE,
+ Manifest.permission.DISABLE_KEYGUARD,
+ Manifest.permission.WAKE_LOCK,
+ Manifest.permission.CALL_PHONE,
+ Manifest.permission.WRITE_CONTACTS,
+ Manifest.permission.READ_CONTACTS,
+ Manifest.permission.RECEIVE_BOOT_COMPLETED,
+ Manifest.permission.PROCESS_OUTGOING_CALLS,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.KILL_BACKGROUND_PROCESSES,
+ Manifest.permission.READ_EXTERNAL_STORAGE
+ };
+ List mPermissionList = new ArrayList<>();
+ private final int mRequestCode = 100;//权限请求码
+
+ private void initPermission() {
+ mPermissionList.clear();//清空已经允许的没有通过的权限
+ //逐个判断是否还有未通过的权限
+ for (int i = 0; i < permissions.length; i++) {
+ if (ContextCompat.checkSelfPermission(this, permissions[i]) !=
+ PackageManager.PERMISSION_GRANTED) {
+ mPermissionList.add(permissions[i]);//添加还未授予的权限到mPermissionList中
+ }
+ }
+ //申请权限
+ if (mPermissionList.size() > 0) {//有权限没有通过,需要申请
+ ActivityCompat.requestPermissions(this, permissions, mRequestCode);
+ } else {
+ //权限已经都通过了,可以将程序继续打开了
+ CONS.requestPermission_ACTION_MANAGE_OVERLAY_PERMISSION(this);
+ CONS.requestPermission_ACTION_MANAGE_WRITE_SETTINGS(this);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ switch (requestCode) {
+ case 101:
+
+ break;
+ case 102:
+
+ break;
+ default:
+
+ boolean hasPermissionDismiss = false;//有权限没有通过
+ if (mRequestCode == requestCode) {
+ for (int i = 0; i < grantResults.length; i++) {
+ if (grantResults[i] == -1) {
+ hasPermissionDismiss = true;
+ Log.e("main", permissions[i]);
+ break;
+ }
+ }
+ }
+ if (hasPermissionDismiss) {//如果有没有被允许的权限
+ showPermissionDialog();
+ } else {
+ //权限已经都通过了,可以将程序继续打开了
+ }
+
+
+ break;
+ }
+
+ }
+
+ AlertDialog mPermissionDialog;
+
+ private void showPermissionDialog() {
+ if (mPermissionDialog == null) {
+ mPermissionDialog = new AlertDialog.Builder(this)
+ .setMessage("已禁用权限,请手动授予")
+ .setPositiveButton("设置", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ cancelPermissionDialog();
+
+ Uri packageURI = Uri.parse("package:" + App.getContext());
+ Intent intent = new Intent(Settings.
+ ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
+ startActivity(intent);
+ }
+ })
+ .setNegativeButton("取消", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ //关闭页面或者做其他操作
+ cancelPermissionDialog();
+ }
+ })
+ .create();
+ }
+ mPermissionDialog.show();
+
+ }
+
+ private void cancelPermissionDialog() {
+ mPermissionDialog.cancel();
+ }
+
+ public void exit2(View view) {
+ Net.login(this.name, this.pw, new HttpUtl.CallBack() {
+ @Override
+ public void onRequestComplete(int cmd, String result, Object orgs) {
+ ActivityMgr.sendMsg(CONS.LOGIN, null);
+ }
+
+ @Override
+ public void onRequestError(int cmd, String result, Object orgs) {
+ UIUtl.toastI("取得token失败");
+ }
+ }, null);
+
+ }
+}
diff --git a/app/src/main/java/com/tianrun/sipcall/login/MainActivity.java b/app/src/main/java/com/tianrun/sipcall/login/MainActivity.java
new file mode 100644
index 0000000..59f9e74
--- /dev/null
+++ b/app/src/main/java/com/tianrun/sipcall/login/MainActivity.java
@@ -0,0 +1,563 @@
+package com.tianrun.sipcall.login;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.media.AudioManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Message;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.GridView;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.viewpager.widget.PagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.qmuiteam.qmui.skin.QMUISkinManager;
+import com.qmuiteam.qmui.util.QMUIDisplayHelper;
+import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet;
+import com.qmuiteam.qmui.widget.dialog.QMUIDialog;
+import com.qmuiteam.qmui.widget.dialog.QMUIDialogAction;
+import com.qmuiteam.qmui.widget.dialog.QMUITipDialog;
+import com.qmuiteam.qmui.widget.popup.QMUIPopups;
+import com.qmuiteam.qmui.widget.popup.QMUIQuickAction;
+import com.qmuiteam.qmui.widget.tab.QMUITabBuilder;
+import com.qmuiteam.qmui.widget.tab.QMUITabIndicator;
+import com.qmuiteam.qmui.widget.tab.QMUITabSegment;
+import com.tianrun.sipcall.App;
+import com.tianrun.sipcall.R;
+import com.tianrun.sipcall.SipEngine;
+import com.tianrun.sipcall.call.CallActivity;
+import com.tianrun.sipcall.call.CreateGroupActivity;
+import com.tianrun.sipcall.call.CreateMeetingActivity;
+import com.tianrun.sipcall.db.DBHead;
+import com.tianrun.sipcall.db.DBUser;
+import com.tianrun.sipcall.net.Net;
+import com.tianrun.sipcall.net.NetPkg;
+import com.tianrun.sipcall.ui.ActivityMgr;
+import com.tianrun.sipcall.ui.TrAdapter;
+import com.tianrun.sipcall.ui.TrBaseActivity;
+import com.tianrun.sipcall.ui.UIUtl;
+import com.tianrun.sipcall.utils.CONS;
+import com.tianrun.sipcall.utils.HttpUtl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MainActivity extends TrBaseActivity {
+ TextView textVolum;
+ TextView textSelfNum;
+ QMUITabSegment mTabSegment;
+ ViewPager mContentViewPager;
+ Button buttonAddGroup;
+ ImageView imageViewAddGroupIcon;
+ View StartMeetingVideo;
+ View StartMeetingVoice;
+ View ButtonDelGroup;
+ // TrAdapter adapter;
+ TrAdapter adapterUser;
+ AudioManager audioManager;
+ ImageView buttonAddVol;
+ ImageView buttonDelVol;
+ DBHead currSelectData;
+
+ GridView gridUser;
+ List