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 allUsers = new ArrayList<>(); + List headers = new ArrayList<>(); + private int mCurrentDialogStyle = com.qmuiteam.qmui.R.style.QMUI_Dialog; + + + private PagerAdapter mPagerAdapter = new PagerAdapter() { + @Override + public boolean isViewFromObject(View view, Object object) { + return view == object; + } + + @Override + public int getCount() { + return headers.size(); + } + + @Override + public Object instantiateItem(final ViewGroup container, int position) { +// ContentPage page = ContentPage.getPage(position); + View view = getPageView(position); +// view.setTag(page); +// ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( +// ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); +// container.addView(view, params); + return view; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + container.removeView((View) object); + } + + @Override + public int getItemPosition(@NonNull Object object) { + View view = (View) object; + return 0; + } + }; + + @RequiresApi(api = Build.VERSION_CODES.M) + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + textVolum = findViewById(R.id.Volumne); + textSelfNum = findViewById(R.id.textViewSelfNum); + mTabSegment = findViewById(R.id.HeadList); + gridUser = findViewById(R.id.GridUser); + mContentViewPager = findViewById(R.id.contentViewPager); + buttonAddGroup = findViewById(R.id.buttonAddGroup); + imageViewAddGroupIcon = findViewById(R.id.imageViewAddGroupIcon); + audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + StartMeetingVoice = findViewById(R.id.StartMeetingVoice); + StartMeetingVideo = findViewById(R.id.StartMeetingVideo); + ButtonDelGroup = findViewById(R.id.ButtonDelGroup); + buttonAddVol = findViewById(R.id.imageViewAddVoice); + buttonAddVol.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + chgVolum(v, true); + return true; + } + }); + buttonDelVol = findViewById(R.id.imageViewDelVoice); + buttonDelVol.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + chgVolum(v, false); + return true; + } + }); + + ButtonDelGroup.setVisibility(View.INVISIBLE); + StartMeetingVoice.setVisibility(View.INVISIBLE); + StartMeetingVideo.setVisibility(View.INVISIBLE); + refreshView(); + + currSelectData = DBHead.newHead4Total(); + currSelectData.isSelected = true; + headers.add(currSelectData); + initTabAndPager(); + Net.getGroups(null, null); + Net.getUsers(null, null); + checkBeforeMeetingState(); + } + + // 处理当前分机号可能有需要加入的会议 + private void checkBeforeMeetingState() { + Net.getUserInfor(DBUser.mySelf.phone, new HttpUtl.CallBack() { + @Override + public void onRequestComplete(int cmd, String result, Object orgs) { + JSONArray array = JSONObject.parseObject(result).getJSONArray("agents"); + JSONObject jo = array.getJSONObject(0); + JSONArray comment = jo.getJSONArray("comment"); + if (comment != null && comment.size() > 0) { + List list = new ArrayList<>(); + for (Object o : comment) { + JSONObject com = (JSONObject) o; + String name = com.getString("topic") + "【" + ((com.getString("type").equals("video_profile")) ? "视频会议" : "语音会议") + "】"; + list.add(name); + } + + UIUtl.showBottomSheetList("您有会议需要加入", list, new QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener() { + @Override + public void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag) { + JSONObject o = comment.getJSONObject(position); + SipEngine.getInstance().CallNumber(o.getString("nbr"), (o.getString("type").equals("video_profile")) ? true : false); + } + }); + } + } + + @Override + public void onRequestError(int cmd, String result, Object orgs) { + } + }, null); + } + + void refreshHeaders() { + DBHead h = headers.get(0); + headers.clear(); + headers.add(h); + headers.addAll(DBHead.allGroups); + + mPagerAdapter.notifyDataSetChanged(); + mTabSegment.reset(); + QMUITabBuilder tabBuilder = mTabSegment.tabBuilder(); + for (int i = 0; i < headers.size(); i++) { + mTabSegment.addTab(tabBuilder.setText(headers.get(i).name).build(this)); + } + mTabSegment.notifyDataChanged(); + } + + private void initTabAndPager() { + mContentViewPager.setAdapter(mPagerAdapter); +// mContentViewPager.setCurrentItem(mDestPage.getPosition(), false); + QMUITabBuilder tabBuilder = mTabSegment.tabBuilder(); + for (int i = 0; i < headers.size(); i++) { + mTabSegment.addTab(tabBuilder.setText(headers.get(i).name).build(this)); + } + int space = QMUIDisplayHelper.dp2px(this, 16); + mTabSegment.setIndicator(new QMUITabIndicator( + QMUIDisplayHelper.dp2px(this, 2), false, true)); + mTabSegment.setMode(QMUITabSegment.MODE_SCROLLABLE); + mTabSegment.setItemSpaceInScrollMode(space); + mTabSegment.setupWithViewPager(mContentViewPager, false); + mTabSegment.setPadding(space, 0, space, 0); + mTabSegment.addOnTabSelectedListener(new QMUITabSegment.OnTabSelectedListener() { + @Override + public void onTabSelected(int index) { + currSelectData = headers.get(index); + if (currSelectData.isAll()) { + ButtonDelGroup.setVisibility(View.INVISIBLE); + StartMeetingVoice.setVisibility(View.INVISIBLE); + StartMeetingVideo.setVisibility(View.INVISIBLE); + } else { + StartMeetingVoice.setVisibility(View.VISIBLE); + StartMeetingVideo.setVisibility(View.VISIBLE); + if (DBUser.mySelf.isManager) { + ButtonDelGroup.setVisibility(View.VISIBLE); + } else { + ButtonDelGroup.setVisibility(View.INVISIBLE); + } + } + + //显示分机 + allUsers.clear(); + if(currSelectData.isAll()) { + allUsers.addAll(DBUser.allUser); + setUsers(); + } else { + setUsers(); + //取得数据 + Net.getGroupMenbers(currSelectData.id, null, currSelectData.id); + } + } + + @Override + public void onTabUnselected(int index) { + } + + @Override + public void onTabReselected(int index) { + } + + @Override + public void onDoubleTap(int index) { + } + }); + } + + + public void refreshView() { +//通话音量 + int max = audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); + int current = audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); + textVolum.setText("音量:" + current); + if (DBUser.mySelf != null && DBUser.mySelf.phone != null) { + textSelfNum.setText("分机号:" + DBUser.mySelf.phone); + } + if (DBUser.mySelf != null && DBUser.mySelf.isManager) { + buttonAddGroup.setVisibility(View.VISIBLE); + imageViewAddGroupIcon.setVisibility(View.VISIBLE); + } else { + buttonAddGroup.setVisibility(View.INVISIBLE); + imageViewAddGroupIcon.setVisibility(View.INVISIBLE); + } + } + + void chgVolum(View v, boolean isAdd) { + handler.postDelayed(new Runnable() { + @Override + public void run() { + if (!v.isPressed()) { + return; + } + if (isAdd) { + addVolume(v); + } else { + delVolume(v); + } + if (v.isPressed()) { + chgVolum(v, isAdd); + } + } + }, 100); + } + + static Runnable runGetUsers = new Runnable() { + @Override + public void run() { + Net.getUsers(null, null); + } + }; + + public View getPageView(int pos) { + return gridUser; + } + + public void getUsersData() { + handler.postDelayed(runGetUsers, 3000); + } + + public class UserViews { + public TextView textViewName; + public TextView textViewNum; + public ImageView imageViewStatus; + public ImageView imageViewPhoneIcon; + public ImageView imageViewAddflag; + } + + public void setUsers() { + if (DBUser.allUser == null) return; + if (adapterUser == null) { + adapterUser = UIUtl.setList(this, gridUser, R.layout.list_item_user, allUsers, new TrAdapter.Callback() { + @Override + public void initCallback(Object data, int position, View prefabView, ViewGroup parent) { + UserViews views = null; + if (prefabView.getTag() == null) { + views = new UserViews(); + views.textViewName = prefabView.findViewById(R.id.textViewName); + views.textViewNum = prefabView.findViewById(R.id.textViewNum); + views.imageViewStatus = prefabView.findViewById(R.id.imageViewStatus); + views.imageViewAddflag = prefabView.findViewById(R.id.imageViewAddflag); + views.imageViewPhoneIcon = prefabView.findViewById(R.id.imageView3PhoneIcon); + prefabView.setTag(views); + } else { + views = (UserViews) (prefabView.getTag()); + } + DBUser d = (DBUser) data; + views.textViewName.setText(d.name); + views.textViewNum.setText(d.phone); + if (d.isOffline()) { + views.imageViewStatus.setColorFilter(Color.GRAY); + } else if (d.isOnline()) { + views.imageViewStatus.setColorFilter(Color.GREEN); + } else if (d.isBusy()) { + views.imageViewStatus.setColorFilter(Color.RED); + } else { + views.imageViewStatus.setColorFilter(Color.GRAY); + } + if (d.isAddFlag) { + views.imageViewPhoneIcon.setVisibility(View.INVISIBLE); + views.imageViewAddflag.setVisibility(View.VISIBLE); + } else { + views.imageViewPhoneIcon.setVisibility(View.VISIBLE); + views.imageViewAddflag.setVisibility(View.INVISIBLE); + } + } + + @Override + public void clickCallback(Object data, AdapterView parent, View view, int position, long id) { + DBUser d = (DBUser) data; + if (d.isAddFlag) { + //TODO: + return; + } + QMUIQuickAction qa = QMUIPopups.quickAction(MainActivity.this, + QMUIDisplayHelper.dp2px(MainActivity.this, 56), + QMUIDisplayHelper.dp2px(MainActivity.this, 56)) + .shadow(true) + .dimAmount(0.5f) + .skinManager(QMUISkinManager.defaultInstance(MainActivity.this)) + .edgeProtection(QMUIDisplayHelper.dp2px(MainActivity.this, 20)) + .addAction(new QMUIQuickAction.Action().icon(R.drawable.voice2).text("语音").onClick( + new QMUIQuickAction.OnClickListener() { + @Override + public void onClick(QMUIQuickAction quickAction, QMUIQuickAction.Action action, int position) { + quickAction.dismiss(); + SipEngine.getInstance().CallNumber(d.phone, false); + } + } + )) + .addAction(new QMUIQuickAction.Action().icon(R.drawable.video).text("视频").onClick( + new QMUIQuickAction.OnClickListener() { + @Override + public void onClick(QMUIQuickAction quickAction, QMUIQuickAction.Action action, int position) { + quickAction.dismiss(); + SipEngine.getInstance().CallNumber(d.phone, true); + } + } + )); + if (DBUser.mySelf.isManager) { + qa.addAction(new QMUIQuickAction.Action().icon(android.R.drawable.ic_delete).text("移除").onClick( + new QMUIQuickAction.OnClickListener() { + @Override + public void onClick(QMUIQuickAction quickAction, QMUIQuickAction.Action action, int position) { + quickAction.dismiss(); + //TODO:移除分机 + } + } + )); + } + qa.show(view); + } + }); + } else { + adapterUser.notifyDataSetChanged(); + } + } + +// public void refreshHeadList() { +// adapter.notifyDataSetChanged(); +// } + + public void call(View view) { + startActivity(new Intent(MainActivity.this, CallActivity.class)); + } + + public void meeting(View view) { + } + + public void message(View view) { + } + + public void report(View view) { + } + +// public void exit(View view) { +// if (ActivityMgr.getActivity(LoginActivity.class) == null) { +// Intent intent = new Intent(this, LoginActivity.class); +// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); +// startActivity(intent); +// } +// finish(); +// SipEngine.getInstance().Unregister(); +// } + + + @Override + protected void onDestroy() { + super.onDestroy(); + } + + @Override + public boolean handleMessage(Message m) { + switch (m.what) { + case CONS.FINISH: + finish(); + break; + case Net.CMD_getUsers: + getUsersData(); + if (currSelectData.isAll()) { + allUsers.clear(); + allUsers.addAll(DBUser.allUser); + setUsers(); + } + break; + case Net.CMD_getGroupMenbers: + NetPkg pkg = (NetPkg)(m.obj); + String oldUUID = pkg.orgs.toString(); + if (!currSelectData.isAll() && oldUUID.equals(currSelectData.id)) { + //TODO:解析数据 + //根据权限来处理 + if (DBUser.mySelf.isManager) { + DBUser u = new DBUser("", "添加分机", "false"); + u.isAddFlag = true; + allUsers.add(u); + } + setUsers(); + } + break; + case Net.CMD_getGroups: + refreshHeaders(); + break; + } + return true; + } + + @Override + protected void onStop() { + super.onStop(); + handler.removeCallbacks(runGetUsers); + } + + public void addVolume(View view) { +// UIUtl.toast("音量加"); +// int max = audioManager.getStreamMaxVolume( AudioManager.STREAM_VOICE_CALL ); + audioManager.adjustStreamVolume(AudioManager.STREAM_VOICE_CALL, AudioManager.ADJUST_RAISE, 0); + int current = audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); + textVolum.setText("音量:" + current); + } + + public void delVolume(View view) { +// UIUtl.toastW("音量减"); + audioManager.adjustStreamVolume(AudioManager.STREAM_VOICE_CALL, AudioManager.ADJUST_LOWER, 0); + int current = audioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); + textVolum.setText("音量:" + current); + } + + public void addGroup(View view) { + startActivity(new Intent(this, CreateGroupActivity.class)); + } + + public void addTmpMeeting(View view) { + startActivity(new Intent(this, CreateMeetingActivity.class)); + } + + public void logout(View v) { + SipEngine.getInstance().onLine = false; + SipEngine.getInstance().isRelogin = true; +// SipEngine.getInstance().Unregister(); +// SipEngine.getInstance().stop(); + Intent intent = new Intent(App.getContext(), LoginActivity.class); + Bundle bundle = new Bundle(); + bundle.putBoolean("donotAutoLogin", true); + intent.putExtras(bundle); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(intent); + finish(); + } + + public void doMeetingVoice4Group(View v) { + if (currSelectData.isAll()) { + return; + } + SipEngine.getInstance().CallNumber(currSelectData.audio_code, false); + } + + public void doMeetingVideo4Group(View v) { + if (currSelectData.isAll()) { + return; + } + SipEngine.getInstance().CallNumber(currSelectData.video_code, true); + } + + public void deleteGroup(View v) { + if (currSelectData.isAll()) { + return; + } + new QMUIDialog.MessageDialogBuilder(this) + .setTitle("提示") + .setMessage("确定要删除分组吗?") + .setSkinManager(QMUISkinManager.defaultInstance(this)) + .addAction("取消", new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + dialog.dismiss(); + } + }) + .addAction(0, "删除", QMUIDialogAction.ACTION_PROP_NEGATIVE, new QMUIDialogAction.ActionListener() { + @Override + public void onClick(QMUIDialog dialog, int index) { + //TODO: + dialog.dismiss(); + } + }) + .create(mCurrentDialogStyle).show(); + } +} diff --git a/app/src/main/java/com/tianrun/sipcall/net/Net.java b/app/src/main/java/com/tianrun/sipcall/net/Net.java new file mode 100644 index 0000000..4e1bdb3 --- /dev/null +++ b/app/src/main/java/com/tianrun/sipcall/net/Net.java @@ -0,0 +1,95 @@ +package com.tianrun.sipcall.net; + +import android.util.Log; + +import com.alibaba.fastjson.JSONObject; +import com.tianrun.sipcall.App; +import com.tianrun.sipcall.db.DBRoot; +import com.tianrun.sipcall.ui.ActivityMgr; +import com.tianrun.sipcall.ui.UIUtl; +import com.tianrun.sipcall.utils.HttpUtl; + +public class Net { + public static String ip = "47.111.20.34"; + public static int port = 29003; + + private static final String TAG = "NetDisPatch"; + + public static final int CMD_login = 7000; + public static final int CMD_getUsers = 7001; + public static final int CMD_createMeeting = 7002; + public static final int CMD_getUserInfor = 7003; + public static final int CMD_getGroups = 7004; + public static final int CMD_getGroupMenbers = 7005; + + static HttpUtl.CallBack _callback = new HttpUtl.CallBack() { + @Override + public void onRequestComplete(int cmd, String result, Object params) { + Log.i(TAG, "onRequestComplete: " + result); + DBRoot.onGetData(cmd, result); + Object[] _params = (Object[])params; + HttpUtl.CallBack callback = (HttpUtl.CallBack)(_params[0]); + Object orgs = _params[1]; + if(ActivityMgr.topActivity != null && callback != null) { + ActivityMgr.topActivity.handler.post(new Runnable() { + @Override + public void run() { + if (callback != null) { + callback.onRequestComplete(cmd, result, orgs); + } + } + }); + } + + ActivityMgr.sendMsg(cmd, new NetPkg(result, orgs)); + } + + @Override + public void onRequestError(int cmd, String result, Object orgs) { + Log.e(TAG, "onRequestError: " + result); + if (orgs != null) { + ((HttpUtl.CallBack) orgs).onRequestError(cmd, result, orgs); + } + UIUtl.toastI(result); + } + }; + + public static void login(String username, String password, HttpUtl.CallBack callBack, Object orgs) { + username = "root"; + password = "12345678aa"; + String url = "http://" + ip + ":" + port + "/api/login?" + "username=" + username + "&" + "password=" + password; + Object[] params = {callBack, orgs}; + HttpUtl.doGetAsyn(url, _callback, CMD_login, params); + } + + public static void getUserInfor(String extn, HttpUtl.CallBack callBack, Object orgs) { + String url = "http://" + ip + ":" + port + "/api/ola/agents?extn=" + extn; + Object[] params = {callBack, orgs}; + HttpUtl.doGetAsyn(url, _callback, CMD_getUserInfor, params); + } + + public static void getUsers(HttpUtl.CallBack callBack, Object orgs) { + String url = "http://" + ip + ":" + port + "/api/ola/agents"; + Object[] params = {callBack, orgs}; + HttpUtl.doGetAsyn(url, _callback, CMD_getUsers, params); + } + + public static void createMeeting(String topic, String description, String members, HttpUtl.CallBack callBack, Object orgs) { + String url = "http://" + ip + ":" + port + "/api/conferences/conference/insert"; + String _params = "topic=" + topic + "&description=" + description + "&members=" + members; + Object[] params = {callBack, orgs}; + HttpUtl.doPostAsyn(url, _params, _callback, CMD_createMeeting, params); + } + + public static void getGroups(HttpUtl.CallBack callBack, Object orgs) { + String url = "http://" + ip + ":" + port + "/api/conferences/conference/list"; + Object[] params = {callBack, orgs}; + HttpUtl.doGetAsyn(url, _callback, CMD_getGroups, params); + } + + public static void getGroupMenbers(String task_uuid, HttpUtl.CallBack callBack, Object orgs) { + String url = "http://" + ip + ":" + port + "/api/conferences/conference/"+task_uuid; + Object[] params = {callBack, orgs}; + HttpUtl.doGetAsyn(url, _callback, CMD_getGroupMenbers, params); + } +} diff --git a/app/src/main/java/com/tianrun/sipcall/net/NetPkg.java b/app/src/main/java/com/tianrun/sipcall/net/NetPkg.java new file mode 100644 index 0000000..68c1b06 --- /dev/null +++ b/app/src/main/java/com/tianrun/sipcall/net/NetPkg.java @@ -0,0 +1,11 @@ +package com.tianrun.sipcall.net; + +public class NetPkg { + public String content; + public Object orgs; + + public NetPkg(String content, Object orgs) { + this.content = content; + this.orgs = orgs; + } +} diff --git a/app/src/main/java/com/tianrun/sipcall/ui/ActivityMgr.java b/app/src/main/java/com/tianrun/sipcall/ui/ActivityMgr.java new file mode 100644 index 0000000..e35a5d5 --- /dev/null +++ b/app/src/main/java/com/tianrun/sipcall/ui/ActivityMgr.java @@ -0,0 +1,36 @@ +package com.tianrun.sipcall.ui; + +import com.tianrun.sipcall.utils.CONS; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentSkipListSet; + +public class ActivityMgr { + public static Map actives = new ConcurrentHashMap<>(); + public static TrBaseActivity topActivity; + public static TrBaseActivity getActivity(Class _class){ + return actives.get(_class); + } + + public static void onShowActivity(TrBaseActivity a) { + topActivity = a; + if(!actives.containsKey(a.getClass())) { + actives.put(a.getClass(), a); + } + } + public static void onHideActivity(TrBaseActivity a) { + actives.remove(a.getClass()); + if(a == topActivity) { + topActivity = null; + } + } + + public static void sendMsg(int menu, Object obj) { + for(TrBaseActivity a : actives.values()) { + CONS.SENDMESSAGETO( a.handler, menu, obj); + } + } +} diff --git a/app/src/main/java/com/tianrun/sipcall/ui/TrAdapter.java b/app/src/main/java/com/tianrun/sipcall/ui/TrAdapter.java new file mode 100644 index 0000000..f88f9d8 --- /dev/null +++ b/app/src/main/java/com/tianrun/sipcall/ui/TrAdapter.java @@ -0,0 +1,67 @@ +package com.tianrun.sipcall.ui; + + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; + +import com.tianrun.sipcall.R; +import com.tianrun.sipcall.login.MainActivity; + +import java.util.List; + + +public class TrAdapter extends BaseAdapter { + + public interface Callback { + void initCallback(Object data, int position, View prefabView, ViewGroup parent); + + void clickCallback(Object data, AdapterView parent, View view, int position, long id); + } + + public List list = null; + private Context context; + private LayoutInflater inflater; + private int convertViewId; + private Callback callback; + + public TrAdapter(Context context, int convertViewId, List list, Callback callback) { + inflater = LayoutInflater.from(context); + this.convertViewId = convertViewId; + this.list = list; + this.context = context; + this.callback = callback; + } + + public List getList() + { + return list; + } + + @Override + public int getCount() { + return list.size(); + } + + @Override + public Object getItem(int position) { + return list.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if(convertView == null) + convertView = inflater.inflate(convertViewId, null); + callback.initCallback(getItem(position), position, convertView, parent); + return convertView; + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tianrun/sipcall/ui/TrBaseActivity.java b/app/src/main/java/com/tianrun/sipcall/ui/TrBaseActivity.java new file mode 100644 index 0000000..ea2cb88 --- /dev/null +++ b/app/src/main/java/com/tianrun/sipcall/ui/TrBaseActivity.java @@ -0,0 +1,34 @@ +package com.tianrun.sipcall.ui; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +public class TrBaseActivity extends AppCompatActivity implements Handler.Callback { + + public Handler handler = new Handler(this); + + @Override + public boolean handleMessage(@NonNull Message msg) { + return false; + } + + + @Override + protected void onResume() { + super.onResume(); + ActivityMgr.onShowActivity(this); + } + + @Override + protected void onStop() { + super.onStop(); + ActivityMgr.onHideActivity(this); + } + + +} diff --git a/app/src/main/java/com/tianrun/sipcall/ui/UIUtl.java b/app/src/main/java/com/tianrun/sipcall/ui/UIUtl.java new file mode 100644 index 0000000..514db91 --- /dev/null +++ b/app/src/main/java/com/tianrun/sipcall/ui/UIUtl.java @@ -0,0 +1,168 @@ +package com.tianrun.sipcall.ui; + +import android.content.Context; +import android.view.View; +import android.widget.AdapterView; +import android.widget.GridView; +import android.widget.Toast; + +import androidx.core.content.ContextCompat; + +import com.qmuiteam.qmui.skin.QMUISkinManager; +import com.qmuiteam.qmui.widget.dialog.QMUIBottomSheet; +import com.qmuiteam.qmui.widget.dialog.QMUITipDialog; +import com.tianrun.sipcall.App; + +import java.util.List; + +public class UIUtl { + public static void toast(String tip) { + if (ActivityMgr.topActivity == null) return; + try { + ActivityMgr.topActivity.handler.post(new Runnable() { + @Override + public void run() { + QMUITipDialog tipDialog = new QMUITipDialog.Builder(ActivityMgr.topActivity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_NOTHING) + .setTipWord(tip) + .create(); + tipDialog.show(); + hideToast(tipDialog); + } + }); + } catch (Exception e) { + System.out.println(e); + } + } + + public static void toastI(String tip) { + if (ActivityMgr.topActivity == null) return; + try { + ActivityMgr.topActivity.handler.post(new Runnable() { + @Override + public void run() { + QMUITipDialog tipDialog = new QMUITipDialog.Builder(ActivityMgr.topActivity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_INFO) + .setTipWord(tip) + .create(); + tipDialog.show(); + hideToast(tipDialog); + } + }); + } catch (Exception e) { + System.out.println(e); + } + } + + public static void toastS(String tip) { + if (ActivityMgr.topActivity == null) return; + try { + ActivityMgr.topActivity.handler.post(new Runnable() { + @Override + public void run() { + QMUITipDialog tipDialog = new QMUITipDialog.Builder(ActivityMgr.topActivity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_SUCCESS) + .setTipWord(tip) + .create(); + tipDialog.show(); + hideToast(tipDialog); + } + }); + } catch (Exception e) { + System.out.println(e); + } + } + + public static void toastW(String tip) { + if (ActivityMgr.topActivity == null) return; + try { + ActivityMgr.topActivity.handler.post(new Runnable() { + @Override + public void run() { + QMUITipDialog tipDialog = new QMUITipDialog.Builder(ActivityMgr.topActivity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_FAIL) + .setTipWord(tip) + .create(); + tipDialog.show(); + hideToast(tipDialog); + } + }); + } catch (Exception e) { + System.out.println(e); + } + } + + public static QMUITipDialog toastLoading(String tip) { + if (ActivityMgr.topActivity == null) return null; + QMUITipDialog tipDialog = new QMUITipDialog.Builder(ActivityMgr.topActivity) + .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) + .setTipWord(tip) + .create(); + tipDialog.show(); +// hideToast(tipDialog); + return tipDialog; + } + + private static void hideToast(QMUITipDialog tipDialog) { + if (ActivityMgr.topActivity == null) return; + ActivityMgr.topActivity.handler.postDelayed(new Runnable() { + @Override + public void run() { + try { + tipDialog.dismiss(); + } catch (Exception e) { + System.out.println(e); + } + } + }, 1200); + } + + public static TrAdapter setList(Context context, GridView grid, int convertViewId, Object list, TrAdapter.Callback callback) { + TrAdapter adapter = new TrAdapter(context, convertViewId, (List) list, callback); + grid.setAdapter(adapter); + grid.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + callback.clickCallback(adapter.getItem(position), parent, view, position, id); + } + }); + return adapter; + } + + + public static void showBottomSheetList( + CharSequence title, + List items, QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener onClick) { + showBottomSheetList(true, true, title, items, false, true, onClick); + } + + public static void showBottomSheetList(boolean gravityCenter, + boolean addCancelBtn, + CharSequence title, + List items, + boolean allowDragDismiss, + boolean withMark, QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener onClick) { + QMUIBottomSheet.BottomListSheetBuilder builder = new QMUIBottomSheet.BottomListSheetBuilder(App.getContext()); + builder.setGravityCenter(gravityCenter) + .setSkinManager(QMUISkinManager.defaultInstance(App.getContext())) + .setTitle(title) + .setAddCancelBtn(addCancelBtn) + .setAllowDrag(allowDragDismiss) + .setNeedRightMark(withMark) + .setOnSheetItemClickListener(new QMUIBottomSheet.BottomListSheetBuilder.OnSheetItemClickListener() { + @Override + public void onClick(QMUIBottomSheet dialog, View itemView, int position, String tag) { + dialog.dismiss(); +// Toast.makeText(getActivity(), "Item " + (position + 1), Toast.LENGTH_SHORT).show(); + onClick.onClick(dialog, itemView, position, tag); + } + }); + if (withMark) { + builder.setCheckedIndex(40); + } + for (int i = 1; i <= items.size(); i++) { + builder.addItem(items.get(i)); + } + builder.build().show(); + } +} diff --git a/app/src/main/java/com/tianrun/sipcall/utils/CONS.java b/app/src/main/java/com/tianrun/sipcall/utils/CONS.java new file mode 100644 index 0000000..1e8dfab --- /dev/null +++ b/app/src/main/java/com/tianrun/sipcall/utils/CONS.java @@ -0,0 +1,166 @@ +package com.tianrun.sipcall.utils; + + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Handler; +import android.os.Message; +import android.provider.Settings; + +import com.tianrun.sipcall.App; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.Date; + + +public class CONS { + + + public static final int LOGIN = 200; + public static final int LOGINSHOW = 201; + public static final int CALLSTATE = 202; + public static final int MEDIASTATE = 203; + public static final int CALLDOWN = 204; + public static final int FINISH = 205; + public static final int SETSPEN = 206; + public static final int SETTEXT = 207; + public static final int INCOMINGMESSAGE = 208;//来短信了 + public static final int showpic = 209; + public static final int LOGINFAILED = 230; + + public static void SENDMESSAGETO(Handler handler, int menu, Object object) { + if (handler != null) { + Message.obtain(handler, menu, object).sendToTarget(); + } + } + + /** + * Java文件操作 获取文件扩展名 + * IMG_20170905_100435.jpg.mp3-->mp3 + * Created on: 2017-9-28 + * Author: lyf + */ + public static String getExtensionName(String filename) { + if ((filename != null) && (filename.length() > 0)) { + int dot = filename.lastIndexOf('.'); + if ((dot >-1) && (dot < (filename.length() - 1))) { + return filename.substring(dot + 1); + } + } + return filename; + } + + /** + * Java文件操作 获取不带扩展名的文件名 + * IMG_20170905_100435.jpg.mp3-->IMG_20170905_100435.jpg + * Created on: 2017-9-28 + * Author: lyf + */ + public static String getFileNameNoEx(String filename) { + if ((filename != null) && (filename.length() > 0)) { + int dot = filename.lastIndexOf('.'); + if ((dot >-1) && (dot < (filename.length()))) { + return filename.substring(0, dot); + } + } + return filename; + } + + + public static final int REQUEST_CODE_ASK_WRITE_SETTINGS = 101;//允许程序读取或写入系统设置 + public static void requestPermission_ACTION_MANAGE_WRITE_SETTINGS(Activity c){ + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){ + if( !Settings.System.canWrite(c)){ + Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS, + Uri.parse("package:" + App.getContext().getPackageName())); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + c.startActivityForResult(intent, REQUEST_CODE_ASK_WRITE_SETTINGS); + }else{ + //有了权限,你要做什么呢?具体的动作 + } + } + } + + + public static final int REQUEST_OVERLAY = 102;//悬浮窗口权限申请 + public static void requestPermission_ACTION_MANAGE_OVERLAY_PERMISSION(Activity c) { + if (Build.VERSION.SDK_INT >= 23) { + if (!Settings.canDrawOverlays(c)) { + Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, + Uri.parse("package:" + App.getContext().getPackageName())); + c.startActivityForResult(intent, REQUEST_OVERLAY); + } else { + + } + } + } + + + public static long forChannel(File f1,File f2) throws Exception{ + long time=new Date().getTime(); + int length=2097152; + FileInputStream in=new FileInputStream(f1); + FileOutputStream out=new FileOutputStream(f2); + FileChannel inC=in.getChannel(); + FileChannel outC=out.getChannel(); + ByteBuffer b=null; + while(true){ + if(inC.position()==inC.size()){ + inC.close(); + outC.close(); + return new Date().getTime()-time; + } + if((inC.size()-inC.position()) params, Map files) + throws IOException { + + String BOUNDARY = java.util.UUID.randomUUID().toString(); + String PREFIX = "--", LINEND = "\r\n"; + String MULTIPART_FROM_DATA = "multipart/form-data"; + String CHARSET = "UTF-8"; + URL uri = new URL(actionUrl); + HttpURLConnection conn = (HttpURLConnection) uri.openConnection(); + conn.setReadTimeout(5 * 1000); + conn.setDoInput(true);// 允许输入 + conn.setDoOutput(true);// 允许输出 + conn.setUseCaches(false); + conn.setRequestMethod("POST"); // Post方式 + conn.setRequestProperty("connection", "keep-alive"); + conn.setRequestProperty("Charsert", "UTF-8"); + conn.setRequestProperty("Content-Type", MULTIPART_FROM_DATA + ";boundary=" + BOUNDARY); + if(token != null) { + conn.setRequestProperty("X-Auth-Token", token); + } + // 首先组拼文本类型的参数 + StringBuilder sb = new StringBuilder(); + for (Map.Entry entry : params.entrySet()) { + sb.append(PREFIX); + sb.append(BOUNDARY); + sb.append(LINEND); + sb.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + LINEND); + sb.append("Content-Type: text/plain; charset=" + CHARSET + LINEND); + sb.append("Content-Transfer-Encoding: 8bit" + LINEND); + sb.append(LINEND); + sb.append(entry.getValue()); + sb.append(LINEND); + } + + DataOutputStream outStream = new DataOutputStream(conn.getOutputStream()); + outStream.write(sb.toString().getBytes()); + + // 发送文件数据 + if (files != null) + for (Map.Entry file : files.entrySet()) { + StringBuilder sb1 = new StringBuilder(); + sb1.append(PREFIX); + sb1.append(BOUNDARY); + sb1.append(LINEND); + sb1.append( + "Content-Disposition: form-data; name=\"file\"; filename=\"" + file.getKey() + "\"" + LINEND); + sb1.append("Content-Type: application/octet-stream; charset=" + CHARSET + LINEND); + sb1.append(LINEND); + outStream.write(sb1.toString().getBytes()); + InputStream is = new FileInputStream(file.getValue()); + byte[] buffer = new byte[1024]; + int len = 0; + while ((len = is.read(buffer)) != -1) { + outStream.write(buffer, 0, len); + } + + is.close(); + outStream.write(LINEND.getBytes()); + } + + // 请求结束标志 + byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes(); + outStream.write(end_data); + outStream.flush(); + + // 得到响应码 + int res = conn.getResponseCode(); + InputStream in = conn.getInputStream(); + InputStreamReader isReader = new InputStreamReader(in); + BufferedReader bufReader = new BufferedReader(isReader); + String line = null; + String data = "OK"; + while ((line = bufReader.readLine()) == null) + data += line; + System.out.println(data); + if (res == 200) { + int ch; + StringBuilder sb2 = new StringBuilder(); + while ((ch = in.read()) != -1) { + sb2.append((char) ch); + } + } + outStream.close(); + conn.disconnect(); + return in.toString(); + } + + /** + * 获取网落图片资源 + * + * @param url + * @return + */ + public static Bitmap getHttpBitmap(String url) { + URL myFileURL; + Bitmap bitmap = null; + try { + myFileURL = new URL(url); + // 获得连接 + HttpURLConnection conn = (HttpURLConnection) myFileURL.openConnection(); + // 设置超时时间为6000毫秒,conn.setConnectionTiem(0);表示没有时间限制 + conn.setConnectTimeout(6000); + // 连接设置获得数据流 + conn.setDoInput(true); + // 不使用缓存 + conn.setUseCaches(false); + // 这句可有可无,没有影响 + // conn.connect(); + // 得到数据流 + InputStream is = conn.getInputStream(); + // 解析得到图片 + bitmap = BitmapFactory.decodeStream(is); + // 关闭数据流 + is.close(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + + return bitmap; + + } + +} + diff --git a/app/src/main/java/com/tianrun/sipcall/utils/logmy.java b/app/src/main/java/com/tianrun/sipcall/utils/logmy.java new file mode 100644 index 0000000..38b36dd --- /dev/null +++ b/app/src/main/java/com/tianrun/sipcall/utils/logmy.java @@ -0,0 +1,11 @@ +package com.tianrun.sipcall.utils; + + +import android.util.Log; + +public class logmy { + public static final String tag = "测试"; + public static void e(String str){ + Log.e(tag,str); + } +} diff --git a/app/src/main/res/drawable-hdpi/add.png b/app/src/main/res/drawable-hdpi/add.png new file mode 100644 index 0000000..f95341f Binary files /dev/null and b/app/src/main/res/drawable-hdpi/add.png differ diff --git a/app/src/main/res/drawable-hdpi/add_bg.png b/app/src/main/res/drawable-hdpi/add_bg.png new file mode 100644 index 0000000..c5a06b9 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/add_bg.png differ diff --git a/app/src/main/res/drawable-hdpi/answerbutton.png b/app/src/main/res/drawable-hdpi/answerbutton.png new file mode 100644 index 0000000..b7a6917 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/answerbutton.png differ diff --git a/app/src/main/res/drawable-hdpi/backup.png b/app/src/main/res/drawable-hdpi/backup.png new file mode 100644 index 0000000..9b2df40 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/backup.png differ diff --git a/app/src/main/res/drawable-hdpi/bg.png b/app/src/main/res/drawable-hdpi/bg.png new file mode 100644 index 0000000..1ca7e94 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/bg.png differ diff --git a/app/src/main/res/drawable-hdpi/bg2.png b/app/src/main/res/drawable-hdpi/bg2.png new file mode 100644 index 0000000..3a5744a Binary files /dev/null and b/app/src/main/res/drawable-hdpi/bg2.png differ diff --git a/app/src/main/res/drawable-hdpi/bg_half.png b/app/src/main/res/drawable-hdpi/bg_half.png new file mode 100644 index 0000000..372775b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/bg_half.png differ diff --git a/app/src/main/res/drawable-hdpi/circle.png b/app/src/main/res/drawable-hdpi/circle.png new file mode 100644 index 0000000..0261958 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/circle.png differ diff --git a/app/src/main/res/drawable-hdpi/empty.png b/app/src/main/res/drawable-hdpi/empty.png new file mode 100644 index 0000000..ab067d9 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/empty.png differ diff --git a/app/src/main/res/drawable-hdpi/hangupbutton.png b/app/src/main/res/drawable-hdpi/hangupbutton.png new file mode 100644 index 0000000..25d03b9 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/hangupbutton.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..288b665 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-hdpi/icon_call.png b/app/src/main/res/drawable-hdpi/icon_call.png new file mode 100644 index 0000000..dcb9f1e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/icon_call.png differ diff --git a/app/src/main/res/drawable-hdpi/input.9.png b/app/src/main/res/drawable-hdpi/input.9.png new file mode 100644 index 0000000..56c7b68 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/input.9.png differ diff --git a/app/src/main/res/drawable-hdpi/noruser.png b/app/src/main/res/drawable-hdpi/noruser.png new file mode 100644 index 0000000..ab49d92 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/noruser.png differ diff --git a/app/src/main/res/drawable-hdpi/onuser.png b/app/src/main/res/drawable-hdpi/onuser.png new file mode 100644 index 0000000..3a604a7 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/onuser.png differ diff --git a/app/src/main/res/drawable-hdpi/phone.png b/app/src/main/res/drawable-hdpi/phone.png new file mode 100644 index 0000000..1098b70 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/phone.png differ diff --git a/app/src/main/res/drawable-hdpi/phone3.png b/app/src/main/res/drawable-hdpi/phone3.png new file mode 100644 index 0000000..b11200c Binary files /dev/null and b/app/src/main/res/drawable-hdpi/phone3.png differ diff --git a/app/src/main/res/drawable-hdpi/play.png b/app/src/main/res/drawable-hdpi/play.png new file mode 100644 index 0000000..3c43cd2 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/play.png differ diff --git a/app/src/main/res/drawable-hdpi/tree_ec.png b/app/src/main/res/drawable-hdpi/tree_ec.png new file mode 100644 index 0000000..5d6bfe9 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/tree_ec.png differ diff --git a/app/src/main/res/drawable-hdpi/tree_ex.png b/app/src/main/res/drawable-hdpi/tree_ex.png new file mode 100644 index 0000000..bdb9352 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/tree_ex.png differ diff --git a/app/src/main/res/drawable-hdpi/video.png b/app/src/main/res/drawable-hdpi/video.png new file mode 100644 index 0000000..b00aaf7 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/video.png differ diff --git a/app/src/main/res/drawable-hdpi/voice.png b/app/src/main/res/drawable-hdpi/voice.png new file mode 100644 index 0000000..2818001 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/voice.png differ diff --git a/app/src/main/res/drawable-hdpi/voice2.png b/app/src/main/res/drawable-hdpi/voice2.png new file mode 100644 index 0000000..36033e5 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/voice2.png differ diff --git a/app/src/main/res/drawable-hdpi/voice_add.png b/app/src/main/res/drawable-hdpi/voice_add.png new file mode 100644 index 0000000..d5d7700 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/voice_add.png differ diff --git a/app/src/main/res/drawable-hdpi/voice_less.png b/app/src/main/res/drawable-hdpi/voice_less.png new file mode 100644 index 0000000..456771f Binary files /dev/null and b/app/src/main/res/drawable-hdpi/voice_less.png differ diff --git a/app/src/main/res/drawable-ldpi/icon_call.png b/app/src/main/res/drawable-ldpi/icon_call.png new file mode 100644 index 0000000..dcb9f1e Binary files /dev/null and b/app/src/main/res/drawable-ldpi/icon_call.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_launcher.png b/app/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..6ae570b Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-mdpi/icon_call.png b/app/src/main/res/drawable-mdpi/icon_call.png new file mode 100644 index 0000000..dcb9f1e Binary files /dev/null and b/app/src/main/res/drawable-mdpi/icon_call.png differ diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..971add5 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable-v24/icon_call.png b/app/src/main/res/drawable-v24/icon_call.png new file mode 100644 index 0000000..dcb9f1e Binary files /dev/null and b/app/src/main/res/drawable-v24/icon_call.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher.png b/app/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..d4fb7cd Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xhdpi/icon_call.png b/app/src/main/res/drawable-xhdpi/icon_call.png new file mode 100644 index 0000000..dcb9f1e Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/icon_call.png differ diff --git a/app/src/main/res/drawable-xxhdpi/callbackground.png b/app/src/main/res/drawable-xxhdpi/callbackground.png new file mode 100644 index 0000000..6c75a8e Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/callbackground.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..85a6081 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_call.png b/app/src/main/res/drawable-xxhdpi/icon_call.png new file mode 100644 index 0000000..dcb9f1e Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_call.png differ diff --git a/app/src/main/res/drawable/bt_bg.xml b/app/src/main/res/drawable/bt_bg.xml new file mode 100644 index 0000000..cd5e750 --- /dev/null +++ b/app/src/main/res/drawable/bt_bg.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bt_bgd.xml b/app/src/main/res/drawable/bt_bgd.xml new file mode 100644 index 0000000..8f1a41e --- /dev/null +++ b/app/src/main/res/drawable/bt_bgd.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/call_answer_background.xml b/app/src/main/res/drawable/call_answer_background.xml new file mode 100644 index 0000000..2780c92 --- /dev/null +++ b/app/src/main/res/drawable/call_answer_background.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/call_hangup_background.xml b/app/src/main/res/drawable/call_hangup_background.xml new file mode 100644 index 0000000..3d8e8e0 --- /dev/null +++ b/app/src/main/res/drawable/call_hangup_background.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/callpad_bg.xml b/app/src/main/res/drawable/callpad_bg.xml new file mode 100644 index 0000000..92d4db5 --- /dev/null +++ b/app/src/main/res/drawable/callpad_bg.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/drawable/corners.xml b/app/src/main/res/drawable/corners.xml new file mode 100644 index 0000000..534d7e0 --- /dev/null +++ b/app/src/main/res/drawable/corners.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/corners3.xml b/app/src/main/res/drawable/corners3.xml new file mode 100644 index 0000000..54ae879 --- /dev/null +++ b/app/src/main/res/drawable/corners3.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/corners_black.xml b/app/src/main/res/drawable/corners_black.xml new file mode 100644 index 0000000..b2b00fa --- /dev/null +++ b/app/src/main/res/drawable/corners_black.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/green_bg.xml b/app/src/main/res/drawable/green_bg.xml new file mode 100644 index 0000000..a2217fc --- /dev/null +++ b/app/src/main/res/drawable/green_bg.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..eed7a42 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/icon_call.png b/app/src/main/res/drawable/icon_call.png new file mode 100644 index 0000000..dcb9f1e Binary files /dev/null and b/app/src/main/res/drawable/icon_call.png differ diff --git a/app/src/main/res/drawable/login_selsctor.xml b/app/src/main/res/drawable/login_selsctor.xml new file mode 100644 index 0000000..fe5171e --- /dev/null +++ b/app/src/main/res/drawable/login_selsctor.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/drawable/max_color_corners.xml b/app/src/main/res/drawable/max_color_corners.xml new file mode 100644 index 0000000..291886a --- /dev/null +++ b/app/src/main/res/drawable/max_color_corners.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/red_bg.xml b/app/src/main/res/drawable/red_bg.xml new file mode 100644 index 0000000..82a4490 --- /dev/null +++ b/app/src/main/res/drawable/red_bg.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_callactivity.xml b/app/src/main/res/layout/activity_callactivity.xml new file mode 100644 index 0000000..ce74bdb --- /dev/null +++ b/app/src/main/res/layout/activity_callactivity.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_gps.xml b/app/src/main/res/layout/activity_gps.xml new file mode 100644 index 0000000..8ab66ca --- /dev/null +++ b/app/src/main/res/layout/activity_gps.xml @@ -0,0 +1,18 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_user.xml b/app/src/main/res/layout/list_item_user.xml new file mode 100644 index 0000000..fe6f894 --- /dev/null +++ b/app/src/main/res/layout/list_item_user.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/list_item_user_select.xml b/app/src/main/res/layout/list_item_user_select.xml new file mode 100644 index 0000000..a268336 --- /dev/null +++ b/app/src/main/res/layout/list_item_user_select.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + +