Prechádzať zdrojové kódy

修改语音通话为SIP

wangjk 4 rokov pred
rodič
commit
89394dd701
100 zmenil súbory, kde vykonal 12496 pridanie a 34 odobranie
  1. 1 0
      .idea/gradle.xml
  2. 1 0
      AmDemo_R/.gitignore
  3. 40 0
      AmDemo_R/build.gradle
  4. BIN
      AmDemo_R/libs/armeabi-v7a/libcheckcpu.so
  5. BIN
      AmDemo_R/libs/armeabi-v7a/libvvsip-v7a-neon.so
  6. BIN
      AmDemo_R/libs/armeabi-v7a/libvvsip-v7a.so
  7. BIN
      AmDemo_R/libs/armeabi/libcheckcpu.so
  8. BIN
      AmDemo_R/libs/armeabi/libvvsip-v5.so
  9. 21 0
      AmDemo_R/proguard-rules.pro
  10. 27 0
      AmDemo_R/src/androidTest/java/com/example/amdemo_r/ExampleInstrumentedTest.java
  11. 68 0
      AmDemo_R/src/main/AndroidManifest.xml
  12. 16 0
      AmDemo_R/src/main/java/com/vvsip/Main3Activity.java
  13. 1261 0
      AmDemo_R/src/main/java/com/vvsip/amdemo/FragmentInVideoCall.java
  14. 359 0
      AmDemo_R/src/main/java/com/vvsip/amdemo/MainActivity.java
  15. 221 0
      AmDemo_R/src/main/java/com/vvsip/amdemo/SplashActivity.java
  16. 273 0
      AmDemo_R/src/main/java/com/vvsip/ansip/AudioCompatibility.java
  17. 307 0
      AmDemo_R/src/main/java/com/vvsip/ansip/AudioInput.java
  18. 157 0
      AmDemo_R/src/main/java/com/vvsip/ansip/AudioOutput.java
  19. 33 0
      AmDemo_R/src/main/java/com/vvsip/ansip/CheckCpu.java
  20. 392 0
      AmDemo_R/src/main/java/com/vvsip/ansip/H264MediaCodecDecoder.java
  21. 189 0
      AmDemo_R/src/main/java/com/vvsip/ansip/H264MediaCodecEncoder.java
  22. 32 0
      AmDemo_R/src/main/java/com/vvsip/ansip/IVvsipService.java
  23. 18 0
      AmDemo_R/src/main/java/com/vvsip/ansip/IVvsipServiceListener.java
  24. 240 0
      AmDemo_R/src/main/java/com/vvsip/ansip/VideoOrientationCompatibility.java
  25. 163 0
      AmDemo_R/src/main/java/com/vvsip/ansip/VvsipAccount.java
  26. 146 0
      AmDemo_R/src/main/java/com/vvsip/ansip/VvsipCall.java
  27. 58 0
      AmDemo_R/src/main/java/com/vvsip/ansip/VvsipDTMF.java
  28. 246 0
      AmDemo_R/src/main/java/com/vvsip/ansip/VvsipMediaCodecFinder.java
  29. 30 0
      AmDemo_R/src/main/java/com/vvsip/ansip/VvsipMediaCodecInfo.java
  30. 6 0
      AmDemo_R/src/main/java/com/vvsip/ansip/VvsipNeighbor.java
  31. 2637 0
      AmDemo_R/src/main/java/com/vvsip/ansip/VvsipService.java
  32. 23 0
      AmDemo_R/src/main/java/com/vvsip/ansip/VvsipServiceBinder.java
  33. 415 0
      AmDemo_R/src/main/java/com/vvsip/ansip/VvsipTask.java
  34. 591 0
      AmDemo_R/src/main/java/com/vvsip/ansip/video/VideoCameraPreview.java
  35. 288 0
      AmDemo_R/src/main/java/com/vvsip/viewsip/VideoDisplay.java
  36. BIN
      AmDemo_R/src/main/res/drawable-hdpi/ic_call_end_white_48dp.png
  37. BIN
      AmDemo_R/src/main/res/drawable-hdpi/ic_launcher.png
  38. BIN
      AmDemo_R/src/main/res/drawable-hdpi/ic_videocam_off_white_48dp.png
  39. BIN
      AmDemo_R/src/main/res/drawable-mdpi/ic_launcher.png
  40. 34 0
      AmDemo_R/src/main/res/drawable-v24/ic_launcher_foreground.xml
  41. BIN
      AmDemo_R/src/main/res/drawable-xhdpi/ic_launcher.png
  42. BIN
      AmDemo_R/src/main/res/drawable-xxhdpi/ic_launcher.png
  43. 170 0
      AmDemo_R/src/main/res/drawable/ic_launcher_background.xml
  44. 169 0
      AmDemo_R/src/main/res/layout/activity_main.xml
  45. 9 0
      AmDemo_R/src/main/res/layout/activity_main3.xml
  46. 85 0
      AmDemo_R/src/main/res/layout/splash_layout.xml
  47. 200 0
      AmDemo_R/src/main/res/layout/video_camera.xml
  48. 5 0
      AmDemo_R/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  49. 5 0
      AmDemo_R/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  50. BIN
      AmDemo_R/src/main/res/mipmap-hdpi/ic_launcher.png
  51. BIN
      AmDemo_R/src/main/res/mipmap-hdpi/ic_launcher_round.png
  52. BIN
      AmDemo_R/src/main/res/mipmap-mdpi/ic_launcher.png
  53. BIN
      AmDemo_R/src/main/res/mipmap-mdpi/ic_launcher_round.png
  54. BIN
      AmDemo_R/src/main/res/mipmap-xhdpi/ic_launcher.png
  55. BIN
      AmDemo_R/src/main/res/mipmap-xhdpi/ic_launcher_round.png
  56. BIN
      AmDemo_R/src/main/res/mipmap-xxhdpi/ic_launcher.png
  57. BIN
      AmDemo_R/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
  58. BIN
      AmDemo_R/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  59. BIN
      AmDemo_R/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
  60. BIN
      AmDemo_R/src/main/res/raw/holdmusic.wav
  61. BIN
      AmDemo_R/src/main/res/raw/ringback.wav
  62. 6 0
      AmDemo_R/src/main/res/values/colors.xml
  63. 87 0
      AmDemo_R/src/main/res/values/preference_values.xml
  64. 71 0
      AmDemo_R/src/main/res/values/strings.xml
  65. 11 0
      AmDemo_R/src/main/res/values/styles.xml
  66. 17 0
      AmDemo_R/src/main/res/xml/advanced_pref.xml
  67. 5 0
      AmDemo_R/src/main/res/xml/pref_media_advanced_audio.xml
  68. 7 0
      AmDemo_R/src/main/res/xml/pref_media_advanced_video.xml
  69. 167 0
      AmDemo_R/src/main/res/xml/pref_media_audio.xml
  70. 62 0
      AmDemo_R/src/main/res/xml/pref_media_video.xml
  71. 21 0
      AmDemo_R/src/main/res/xml/pref_sip_account.xml
  72. 66 0
      AmDemo_R/src/main/res/xml/pref_sip_optional.xml
  73. 17 0
      AmDemo_R/src/test/java/com/example/amdemo_r/ExampleUnitTest.java
  74. 2 2
      home/build.gradle
  75. 4 1
      home/src/main/AndroidManifest.xml
  76. 16 0
      home/src/main/code/com/wdkl/ncs/android/component/home/SipUtil/SipCallBack.java
  77. 626 0
      home/src/main/code/com/wdkl/ncs/android/component/home/SipUtil/SipHelper.java
  78. 683 0
      home/src/main/code/com/wdkl/ncs/android/component/home/SipUtil/SipHelperUtil.java
  79. 701 0
      home/src/main/code/com/wdkl/ncs/android/component/home/SipUtil/SipHelperUtil2.java
  80. 20 6
      home/src/main/code/com/wdkl/ncs/android/component/home/activity/RTCVoipAudioRingingActivity.java
  81. 225 0
      home/src/main/code/com/wdkl/ncs/android/component/home/activity/SipVoipAudioActivity.kt
  82. 180 0
      home/src/main/code/com/wdkl/ncs/android/component/home/activity/SipVoipAudioRingingActivity.kt
  83. 9 2
      home/src/main/code/com/wdkl/ncs/android/component/home/activity/WatchCallRecordsActivity.kt
  84. 1 1
      home/src/main/code/com/wdkl/ncs/android/component/home/activity/WatchContactsActivity.kt
  85. 57 0
      home/src/main/code/com/wdkl/ncs/android/component/home/activity/WatchEventDetailActivity.kt
  86. 148 8
      home/src/main/code/com/wdkl/ncs/android/component/home/activity/WatchHomeActivity.kt
  87. 25 1
      home/src/main/code/com/wdkl/ncs/android/component/home/activity/WatchRegisterActivity.kt
  88. 7 0
      home/src/main/code/com/wdkl/ncs/android/component/home/activity/WatchUserSettingActivity.java
  89. 38 0
      home/src/main/code/com/wdkl/ncs/android/component/home/broadcast/BatteryBroadcastReceiver.java
  90. 14 0
      home/src/main/code/com/wdkl/ncs/android/component/home/util/EthernetWifiCallBack.java
  91. 6 2
      home/src/main/code/com/wdkl/ncs/android/component/home/util/NetHelper.java
  92. 76 0
      home/src/main/res/layout/activity_sip_voip_audio.xml
  93. 105 0
      home/src/main/res/layout/activity_sip_voip_audio_ringing.xml
  94. 8 2
      home/src/main/res/layout/user_setting_layout.xml
  95. 24 4
      home/src/main/res/layout/watch_activity_event_detail.xml
  96. 36 1
      home/src/main/res/layout/watch_activity_home.xml
  97. 7 0
      home/src/main/res/layout/watch_activity_register.xml
  98. 4 4
      home/src/main/res/layout/watch_activity_voice_calls.xml
  99. 1 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/api/UrlManager.kt
  100. 0 0
      middleware/src/main/code/com/wdkl/ncs/android/middleware/common/Constants.kt

+ 1 - 0
.idea/gradle.xml

@@ -8,6 +8,7 @@
         <option name="modules">
           <set>
             <option value="$PROJECT_DIR$" />
+            <option value="$PROJECT_DIR$/AmDemo_R" />
             <option value="$PROJECT_DIR$/app" />
             <option value="$PROJECT_DIR$/common" />
             <option value="$PROJECT_DIR$/extra" />

+ 1 - 0
AmDemo_R/.gitignore

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

+ 40 - 0
AmDemo_R/build.gradle

@@ -0,0 +1,40 @@
+//apply plugin: 'com.android.application'
+apply plugin: 'com.android.library'
+android {
+    compileSdkVersion 30
+    buildToolsVersion "30.0.2"
+
+
+    defaultConfig {
+//        applicationId "com.example.amdemo_r"
+        minSdkVersion 15
+        targetSdkVersion 30
+        versionCode 1
+        versionName "1.0"
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    sourceSets {
+        main {
+            jniLibs.srcDirs = ['libs']
+        }
+    }
+
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+    implementation 'androidx.appcompat:appcompat:1.0.2'
+    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+    testImplementation 'junit:junit:4.12'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
+}

BIN
AmDemo_R/libs/armeabi-v7a/libcheckcpu.so


BIN
AmDemo_R/libs/armeabi-v7a/libvvsip-v7a-neon.so


BIN
AmDemo_R/libs/armeabi-v7a/libvvsip-v7a.so


BIN
AmDemo_R/libs/armeabi/libcheckcpu.so


BIN
AmDemo_R/libs/armeabi/libvvsip-v5.so


+ 21 - 0
AmDemo_R/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

+ 27 - 0
AmDemo_R/src/androidTest/java/com/example/amdemo_r/ExampleInstrumentedTest.java

@@ -0,0 +1,27 @@
+package com.example.amdemo_r;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+        assertEquals("com.example.amdemo_r", appContext.getPackageName());
+    }
+}

+ 68 - 0
AmDemo_R/src/main/AndroidManifest.xml

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.amdemo_r">
+
+
+    <uses-permission android:name="android.permission.INTERNET" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.RECORD_AUDIO" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.READ_CONTACTS" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.WAKE_LOCK" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.CAMERA" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.BLUETOOTH" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.VIBRATE" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.BROADCAST_STICKY" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" >
+    </uses-permission>
+    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS">
+    </uses-permission>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
+    </uses-permission>
+
+
+    <application
+        android:allowBackup="true"
+        android:label="@string/app_name">
+
+
+<!--        android:allowBackup="true"-->
+<!--        android:icon="@mipmap/ic_launcher"-->
+<!--        android:label="@string/app_name"-->
+<!--        android:roundIcon="@mipmap/ic_launcher_round"-->
+<!--        android:supportsRtl="true"-->
+<!--        android:theme="@style/AppTheme">-->
+<!--        <activity android:name="com.vvsip.Main3Activity">-->
+<!--            <intent-filter>-->
+<!--                <action android:name="android.intent.action.MAIN" />-->
+
+<!--                <category android:name="android.intent.category.LAUNCHER" />-->
+<!--            </intent-filter>-->
+<!--        </activity>-->
+
+        <service
+            android:name="com.vvsip.ansip.VvsipService"
+            android:icon="@drawable/ic_launcher" >
+        </service>
+
+    </application>
+
+</manifest>

+ 16 - 0
AmDemo_R/src/main/java/com/vvsip/Main3Activity.java

@@ -0,0 +1,16 @@
+package com.vvsip;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.os.Bundle;
+
+import com.example.amdemo_r.R;
+
+public class Main3Activity extends AppCompatActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main3);
+    }
+}

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 1261 - 0
AmDemo_R/src/main/java/com/vvsip/amdemo/FragmentInVideoCall.java


+ 359 - 0
AmDemo_R/src/main/java/com/vvsip/amdemo/MainActivity.java

@@ -0,0 +1,359 @@
+package com.vvsip.amdemo;//package com.vvsip.amdemo;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.example.amdemo_r.R;
+import com.vvsip.ansip.IVvsipService;
+import com.vvsip.ansip.IVvsipServiceListener;
+import com.vvsip.ansip.VvsipCall;
+import com.vvsip.ansip.VvsipService;
+import com.vvsip.ansip.VvsipTask;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/*
+ * ������Activity
+ */
+public class MainActivity extends Activity implements
+		View.OnClickListener, IVvsipServiceListener {
+
+	private EditText serverAddr;
+	private EditText userphone;
+	private EditText passwd;
+	private EditText callee;
+
+	private TextView callStatus;
+
+	private Button mButton_register;
+	private Button mButton_audioCall;
+	private Button mButton_endCall;
+	private Button mButton_offHook;
+	private Button mButton_addVideo;
+	private EditText dtmf;
+	private Button mButton_sendDTMF;
+
+
+	static String mTag = "MainActivity";
+	private List<VvsipCall> mVvsipCalls = null;
+
+	private String lan_callee;
+
+	@Override
+	protected void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		setContentView(R.layout.activity_main);
+
+		serverAddr = (EditText) findViewById(R.id.server_addr);
+		userphone = (EditText) findViewById(R.id.userphone);
+		passwd = (EditText) findViewById(R.id.passwd);
+		callee = (EditText) findViewById(R.id.callee);
+
+		callStatus = (TextView) findViewById(R.id.call_status);
+
+		mButton_register = (Button) findViewById(R.id.register);
+		mButton_audioCall = (Button) findViewById(R.id.audio_call);
+		mButton_endCall = (Button) findViewById(R.id.end_call);
+		mButton_offHook = (Button) findViewById(R.id.off_hook);
+		mButton_addVideo = (Button) findViewById(R.id.add_video);
+		mButton_sendDTMF = (Button) findViewById(R.id.send_dtmf);
+
+
+
+		mButton_register.setOnClickListener(this);
+		mButton_audioCall.setOnClickListener(this);
+		mButton_endCall.setOnClickListener(this);
+		mButton_offHook.setOnClickListener(this);
+		mButton_addVideo.setOnClickListener(this);
+		mButton_sendDTMF.setOnClickListener(this);
+
+		IVvsipService _service = VvsipService.getService();
+		Log.i(mTag, "lifecycle // _service");
+		if (_service != null) {
+
+			_service.addListener(this);
+			_service.setMessageHandler(messageHandler);
+			Log.i(mTag, "lifecycle // addListener");
+		} else {
+			Log.i(mTag, "lifecycle // _service==null");
+		}
+
+		if (mVvsipCalls == null) {
+			mVvsipCalls = new ArrayList<VvsipCall>();
+		}
+
+	}
+
+	@Override
+	public void onDestroy() {
+		IVvsipService _service = VvsipService.getService();
+		if (_service != null)
+			_service.removeListener(this);
+		if (mVvsipCalls != null) {
+			mVvsipCalls.clear();
+			mVvsipCalls = null;
+		}
+		super.onDestroy();
+		Log.i(mTag, "lifecycle // onDestroy");
+	}
+
+
+	@Override
+	public void onNewVvsipCallEvent(final VvsipCall call) {
+
+		MainActivity.this.runOnUiThread(new Runnable() {
+			public void run() {
+				try {
+					if (call == null) {
+						return;
+					}
+
+					if (mVvsipCalls == null)
+						return;
+					mVvsipCalls.add(call);
+
+					if (Build.VERSION.SDK_INT >= 5) {
+						getWindow()
+								.addFlags( // WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+											// |
+										WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+												| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+												| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+					}
+
+				} catch (Exception e) {
+					Log.e(mTag, "onNewVvsipCallEvent: " + e);
+				}
+			}
+		});
+	}
+
+	@Override
+	public void onStatusVvsipCallEvent(VvsipCall call) {
+		Log.d("SIPTES","call :"+call.mState);
+	}
+
+	@Override
+	public void onRemoveVvsipCallEvent(final VvsipCall call) {
+
+		MainActivity.this.runOnUiThread(new Runnable() {
+			public void run() {
+				try {
+					if (call == null) {
+						return;
+					}
+
+					// 4 crash detected here for 4.0.9 with mVvsipCalls=NULL
+					if (mVvsipCalls == null)
+						return;
+					mVvsipCalls.remove(call);
+
+					if (mVvsipCalls.size() == 0) {
+						if (Build.VERSION.SDK_INT >= 5) {
+							getWindow()
+									.clearFlags( // WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+													// |
+											WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+													| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+													| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+						}
+					}
+				} catch (Exception e) {
+					Log.e(mTag, "onRemoveVvsipCallEvent: " + e);
+				}
+			}
+		});
+	}
+
+	@Override
+	public void onRegistrationEvent(final int rid, final String remoteUri,
+			final int code, String reason) {
+
+	}
+
+	@SuppressLint("HandlerLeak")
+	private Handler messageHandler = new Handler() {
+
+		@Override
+		public void handleMessage(Message msg) {
+			Log.i(mTag, "VvsipEvent received (?" + msg.what + " " + msg.arg1
+					+ " " + msg.arg2 + ")\n");
+			Log.i(mTag, "#" + msg.obj);
+			callStatus.setText("" + msg.obj + callStatus.getText());
+
+			if(msg.obj.toString().contains("autocall")){ //��������
+				VvsipCall pCall = null;
+				Log.e(mTag, "onClick1");
+				for (VvsipCall _pCall : mVvsipCalls) {
+					if(_pCall.cid > 0)
+						Log.e(mTag, "state#"+_pCall.mState);
+					if (_pCall.cid > 0 && _pCall.mState <= 2) {
+						pCall = _pCall;
+						break;
+					}
+				}
+				Log.e(mTag, "onClick2");
+				if (pCall == null)
+					return;
+				Log.e(mTag, "onClick3#"+pCall.mState);
+				IVvsipService _service = VvsipService.getService();
+				if (_service == null)
+					return;
+				VvsipTask _vvsipTask = _service.getVvsipTask();
+				if (_vvsipTask == null)
+					return;
+				pCall.stop();
+				_service.setSpeakerModeOff();
+			}
+
+
+		}
+
+	};
+
+	@Override
+	public void onClick(View v) {
+
+		/*
+		 * ע��
+		 */
+		if (v == mButton_register) {
+			MainActivity.this.runOnUiThread(new Runnable() {
+				public void run() {
+					IVvsipService _service = VvsipService.getService();
+					if (_service != null) {
+						// _service.StartVvsipLayer();
+
+						_service.register(serverAddr.getText().toString(),
+								userphone.getText().toString(), passwd
+										.getText().toString());
+					}
+				}
+			});
+			return;
+		}
+		/*
+		 * �������
+		 */
+		if (v == mButton_audioCall) {
+			if (callee.getText().length() == 0)
+				return;
+			MainActivity.this.runOnUiThread(new Runnable() {
+				public void run() {
+					IVvsipService _service = VvsipService.getService();
+					if (_service == null)
+						return;
+					_service.initiateOutgoingCall(callee.getText().toString(),"");
+				}
+			});
+			return;
+		}
+		/*
+		 * ����ͨ��
+		 */
+		if (v == mButton_endCall) {
+			VvsipCall pCall = null;
+			Log.e(mTag, "onClick1");
+			for (VvsipCall _pCall : mVvsipCalls) {
+				if(_pCall.cid > 0)
+					Log.e(mTag, "state#"+_pCall.mState);
+				if (_pCall.cid > 0 && _pCall.mState <= 2) {
+					pCall = _pCall;
+					break;
+				}
+			}
+			Log.e(mTag, "onClick2");
+			if (pCall == null)
+				return;
+			Log.e(mTag, "onClick3#"+pCall.mState);
+			IVvsipService _service = VvsipService.getService();
+			if (_service == null)
+				return;
+			VvsipTask _vvsipTask = _service.getVvsipTask();
+			if (_vvsipTask == null)
+				return;
+			pCall.stop();
+			_service.setSpeakerModeOff();
+			_service.stopPlayer();   //���йҶϣ�����ֹͣ;��������Closed��Released״̬�л������������ġ�
+									 //����HDL�DZ�û��Ч����������������һ�� ,By 20160822
+			_service.setAudioNormalMode();
+			return;
+		}
+
+		/*
+		 * �����绰
+		 */
+		if (v == mButton_offHook) {
+			for (VvsipCall _pCall : mVvsipCalls) {
+				if (_pCall.cid > 0 && _pCall.mState < 2 && _pCall.mIncomingCall) {
+					// ANSWER EXISTING CALL
+					int i = _pCall.answer(200, 1);
+					IVvsipService _service = VvsipService.getService();
+					if (_service != null) {
+						if (i >= 0) {
+							_service.stopPlayer();
+							_service.setSpeakerModeOff();
+							_service.setAudioInCallMode();
+						}
+					}
+					break;
+				}
+			}
+		}
+		/*
+		 * ������Ƶͨ��
+		 */
+		if (v == mButton_addVideo) {
+			MainActivity.this.runOnUiThread(new Runnable() {
+				public void run() {
+					Fragment fragment = null;
+					fragment = new FragmentInVideoCall();
+					FragmentManager frgManager = getFragmentManager();
+					FragmentTransaction ft = frgManager.beginTransaction()
+							.replace(R.id.content_frame, fragment);
+
+					ft.addToBackStack(null);
+					ft.commit();
+					FrameLayout fly = (FrameLayout) MainActivity.this
+							.findViewById(R.id.content_frame);
+					fly.bringToFront();
+				}
+			});
+		}
+
+		/*
+		 * ����DTMF
+		 */
+		if (v == mButton_sendDTMF) {
+			if (dtmf.getText().length() == 0)
+				return;
+			MainActivity.this.runOnUiThread(new Runnable() {
+				public void run() {
+					IVvsipService _service = VvsipService.getService();
+					if (_service == null)
+						return;
+					_service.sendDTMF(dtmf.getText().toString());
+				}
+			});
+			return;
+		}
+
+	}
+}

+ 221 - 0
AmDemo_R/src/main/java/com/vvsip/amdemo/SplashActivity.java

@@ -0,0 +1,221 @@
+package com.vvsip.amdemo;///*
+//  vvphone is a SIP app for android.
+//  vvsip is a SIP library for softphone (SIP -rfc3261-)
+//  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+// */
+
+//package com.vvsip.amdemo;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import com.example.amdemo_r.R;
+import com.vvsip.ansip.IVvsipService;
+import com.vvsip.ansip.IVvsipServiceListener;
+import com.vvsip.ansip.VvsipCall;
+import com.vvsip.ansip.VvsipService;
+import com.vvsip.ansip.VvsipServiceBinder;
+import com.vvsip.ansip.VvsipTask;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.text.Html;
+import android.text.method.LinkMovementMethod;
+import android.text.method.MovementMethod;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.TextView;
+import android.os.IBinder;
+
+public class SplashActivity extends Activity implements IVvsipServiceListener {
+
+	protected int _splashTime = 3000;
+	protected Handler _exitHandler = null;
+	protected Runnable _exitRunnable = null;
+	protected Handler _startServiceHandler = null;
+	protected Runnable _startServiceRunnable = null;
+
+	private ServiceConnection connection;
+
+	private TextView mTextView_link;
+	private TextView mTextView_licenselink;
+	private TextView myVersion;
+
+	/** Called when the activity is first created. */
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		Log.i("ActivitySplash", "lifecycle // onCreate");
+		super.onCreate(savedInstanceState);
+		setContentView(R.layout.splash_layout);
+
+		mTextView_link = (TextView) findViewById(R.id.TextView_link);
+		if (mTextView_link != null) {
+			MovementMethod lMM = LinkMovementMethod.getInstance();
+			if (lMM != null) {
+				mTextView_link.setMovementMethod(lMM);
+				mTextView_link.setText(Html.fromHtml("www.vvsip.com"));
+			}
+			mTextView_licenselink = (TextView) findViewById(R.id.TextView_licencelink);
+			if (lMM != null) {
+				mTextView_licenselink.setMovementMethod(lMM);
+				mTextView_licenselink.setText(Html.fromHtml("vvsip.com"));
+			}
+		}
+
+		myVersion = (TextView) findViewById(R.id.my_version);
+		SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
+		String today = formatter.format(new Date());
+		if(today.compareTo("2016-02-08")>0){
+			myVersion.setVisibility(View.VISIBLE);
+			myVersion.setText(myVersion.getText()+"\n"+"This version has expired.Please contact QQ272108638");
+		}else{
+			myVersion.setVisibility(View.GONE);
+		}
+
+		// Runnable exiting the splash screen and launching the menu
+		_exitRunnable = new Runnable() {
+			public void run() {
+				exitSplash();
+			}
+		};
+		// Run the exitRunnable in in _splashTime ms
+		_exitHandler = new Handler();
+
+		IVvsipService _service = VvsipService.getService();
+		if (_service != null) {
+			_exitHandler.postDelayed(_exitRunnable, 0);
+			return;
+		}
+
+		_exitHandler.postDelayed(_exitRunnable, _splashTime);
+
+		_startServiceHandler = new Handler();
+
+		_startServiceRunnable = new Runnable() {
+			public void run() {
+
+				Intent intent = new Intent(SplashActivity.this.getApplicationContext(), VvsipService.class);
+				startService(intent);
+
+				connection = new ServiceConnection() {
+					public void onServiceConnected(ComponentName name, IBinder service) {
+						Log.i("ActivitySplash", "Connected!");
+						IVvsipService _service = ((VvsipServiceBinder) service).getService();
+						_service.addListener(SplashActivity.this);
+					}
+
+					public void onServiceDisconnected(ComponentName name) {
+						Log.i("ActivitySplash", "Disconnected!");
+					}
+				};
+
+				bindService(intent, connection, Context.BIND_AUTO_CREATE);
+				Log.i("ActivitySplash", "bindService done!");
+			}
+		};
+
+		_startServiceHandler.postDelayed(_startServiceRunnable, 0);
+	}
+
+	@Override
+	public void onDestroy() {
+		Log.i("ActivitySplash", "lifecycle // onDestroy");
+		super.onDestroy();
+
+		IVvsipService _service = VvsipService.getService();
+		if (_service != null) {
+			_service.removeListener(this);
+		}
+
+		_exitHandler.removeCallbacks(_startServiceRunnable);
+		_exitHandler.removeCallbacks(_exitRunnable);
+		if (connection != null) {
+			unbindService(connection);
+			connection = null;
+		}
+	}
+
+	@Override
+	public boolean onTouchEvent(MotionEvent event) {
+		if (event.getAction() == MotionEvent.ACTION_DOWN) {
+			// Remove the exitRunnable callback from the handler queue
+			_exitHandler.removeCallbacks(_exitRunnable);
+			// Run the exit code manually
+			exitSplash();
+		}
+		return true;
+	}
+
+	private void exitSplash() {
+		Log.i("ActivitySplash", "lifecycle // exitSplash");
+		VvsipTask vvsipTask = VvsipTask.getVvsipTask();
+		if (vvsipTask != null && VvsipTask.global_failure != 0) {
+			final AlertDialog.Builder b = new AlertDialog.Builder(this);
+			b.setIcon(R.drawable.ic_launcher);
+			b.setTitle(getString(R.string.app_name));
+			b.setMessage("global_installation_failure");
+
+			b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+				public void onClick(DialogInterface dialog, int whichButton) {
+					finish();
+				}
+			});
+			b.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+				public void onClick(DialogInterface dialog, int whichButton) {
+					finish();
+				}
+			});
+			b.show();
+
+			Intent intent = new Intent(Intent.ACTION_MAIN);
+			intent.setClass(this.getApplicationContext(), VvsipService.class);
+			stopService(intent);
+		} else {
+			finish();
+
+			Intent intent = new Intent();
+			intent.setClass(SplashActivity.this, MainActivity.class);
+			startActivity(intent);
+		}
+	}
+
+	@Override
+	public void onNewVvsipCallEvent(VvsipCall call) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void onRemoveVvsipCallEvent(VvsipCall call) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void onStatusVvsipCallEvent(VvsipCall call) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void onRegistrationEvent(int rid, String remote_uri, final int code, String reason) {
+		SplashActivity.this.runOnUiThread(new Runnable() {
+			public void run() {
+				if (code >= 200 && code < 300) {
+					// Remove the exitRunnable callback from the handler queue
+					_exitHandler.removeCallbacks(_exitRunnable);
+					// Run the exit code manually
+					exitSplash();
+				}
+			}
+		});
+	}
+}

+ 273 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/AudioCompatibility.java

@@ -0,0 +1,273 @@
+package com.vvsip.ansip;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.MediaRecorder;
+import android.os.Build;
+import android.util.Log;
+
+import java.util.Locale;
+
+/*
+ * 语音兼容性解决方案,此类不必改动,可直接使用
+ */
+public class AudioCompatibility {
+
+    public static int mAudiomanager_mode_internal = AudioManager.MODE_IN_COMMUNICATION;
+    public static int mAudiomanager_mode_speakermode = AudioManager.MODE_IN_COMMUNICATION;
+    public static int mAudiomanager_wa_galaxyS = -1; 
+    public static int mAudiomanager_stream_type = AudioManager.STREAM_VOICE_CALL;
+    public static int mAudiomanager_audio_source_internal = MediaRecorder.AudioSource.MIC;
+    public static int mAudiomanager_audio_source_speakermode = MediaRecorder.AudioSource.MIC;
+    
+    static public void configureAudioManagerMode(Context context)
+    {
+		Log.i("AudioCompatibility", "API level is " + Build.VERSION.SDK_INT);
+    	
+        if (Build.VERSION.SDK_INT<=4)
+			mAudiomanager_mode_internal = AudioManager.MODE_IN_CALL;
+        else if (Build.VERSION.SDK_INT<10)
+			mAudiomanager_mode_internal = AudioManager.MODE_IN_CALL;
+        else if (Build.VERSION.SDK_INT<11)
+			mAudiomanager_mode_internal = AudioManager.MODE_NORMAL;
+        else 
+			mAudiomanager_mode_internal = AudioManager.MODE_IN_COMMUNICATION;
+
+        //TESTED
+    	mAudiomanager_wa_galaxyS = -1;
+        if (Build.VERSION.SDK_INT<10)
+        {
+    		if (Build.DEVICE.toUpperCase(Locale.US).startsWith("GT-I9000")
+    				|| Build.DEVICE.toUpperCase(Locale.US).startsWith("SHW-M110S")) {
+            	//low gain for MIC in all use-case: (wrong for speacker mode)
+            	mAudiomanager_mode_internal = AudioManager.MODE_IN_CALL;
+            	//setSpeakerphoneOn -> will use WRONG microphone with above code in speaker mode
+            	//setSpeakerphoneOff -> will use CORRECT microphone with above code in earpiece mode
+
+            	//YOU CAN USE THIS HACK INSTEAD TO HAVE HIGHER GAIN IN SPEAKER MODE:
+            	//high gain for MIC in ALL use-case: (wrong for earpiece mode)
+            	// mAudiomanager_wa_galaxyS = AudioManager.MODE_IN_CALL;
+            	// mAudiomanager_mode_internal = AudioManager.MODE_NORMAL;
+        	}
+        }
+
+        //UNTESTED
+        if (Build.VERSION.SDK_INT<10 && Build.DEVICE.toUpperCase(Locale.US).startsWith("GT-P1000")) {
+        	//low gain for MIC in all use-case: (wrong for speacker mode)
+        	mAudiomanager_mode_internal = AudioManager.MODE_IN_CALL;
+        	//setSpeakerphoneOn -> will use WRONG microphone with above code in speaker mode
+        	//setSpeakerphoneOff -> will use CORRECT microphone with above code in earpiece mode
+
+        	//high gain for MIC in ALL use-case: (wrong for earpiece mode)
+        	// mAudiomanager_wa_galaxyS = AudioManager.MODE_IN_CALL;
+        	// mAudiomanager_mode_internal = AudioManager.MODE_NORMAL;
+        }
+        //UNTESTED
+        if (Build.VERSION.SDK_INT<10 && Build.DEVICE.toUpperCase(Locale.US).startsWith("GT-S5660")) {
+        	//low gain for MIC in all use-case: (wrong for speacker mode)
+        	mAudiomanager_mode_internal = AudioManager.MODE_IN_CALL;
+        	//setSpeakerphoneOn -> will use WRONG microphone with above code in speaker mode
+        	//setSpeakerphoneOff -> will use CORRECT microphone with above code in earpiece mode
+
+        	//high gain for MIC in ALL use-case: (wrong for earpiece mode)
+        	// mAudiomanager_wa_galaxyS = AudioManager.MODE_IN_CALL;
+        	// mAudiomanager_mode_internal = AudioManager.MODE_NORMAL;
+        }
+
+        //TESTED FOR "GT-P1010" (default)
+		//if (Build.VERSION.SDK_INT<10 && android.os.Build.DEVICE.toUpperCase(Locale.US).startsWith("GT-P1010"))
+        //	mAudiomanager_mode_internal = AudioManager.MODE_IN_CALL; // this allow to have CORRECT microphone gain
+
+        if ( Build.BRAND.equalsIgnoreCase("Samsung") && (8 == Build.VERSION.SDK_INT)) {
+            // Set Samsung specific VoIP mode for 2.2 devices
+        	AudioManager mAManager;
+            mAManager = ((AudioManager)context.getSystemService(Context.AUDIO_SERVICE));
+        	int mode = 4;
+        	mAManager.setMode(mode);
+            if (mAManager.getMode() == mode) {
+        		Log.i("AudioCompatibility", "Samsung 2.2 detected // AudioManager.4 support detected");
+        		mAudiomanager_mode_internal = mode;
+        	}
+        	mAManager.setMode(AudioManager.MODE_NORMAL);
+        }
+
+        if ( (9 == Build.VERSION.SDK_INT || 10 == Build.VERSION.SDK_INT) ) {
+        	AudioManager mAManager;
+            mAManager = ((AudioManager)context.getSystemService(Context.AUDIO_SERVICE));
+        	int mode = AudioManager.MODE_IN_COMMUNICATION;
+        	mAManager.setMode(mode);
+            if (mAManager.getMode() == mode) {
+        		Log.i("AudioCompatibility", "Android 2.3.x  // AudioManager.MODE_IN_COMMUNICATION support detected");
+        		mAudiomanager_mode_internal = mode;
+        	}
+        	mAManager.setMode(AudioManager.MODE_NORMAL);
+        }
+
+        //UNTESTED
+        if (Build.BRAND.equalsIgnoreCase("sdg"))
+			mAudiomanager_mode_internal = AudioManager.MODE_IN_COMMUNICATION;
+
+        if (Build.MODEL.equalsIgnoreCase("Milestone"))
+			mAudiomanager_mode_internal = AudioManager.MODE_NORMAL;
+
+        if (Build.MODEL.equalsIgnoreCase("Titanium"))
+        	mAudiomanager_mode_internal = AudioManager.MODE_NORMAL;
+
+        if (Build.BRAND.equalsIgnoreCase("motorola") && mAudiomanager_mode_internal == AudioManager.MODE_IN_CALL) {
+        	mAudiomanager_mode_internal = AudioManager.MODE_NORMAL;
+        }
+
+
+        if (Build.MODEL.equalsIgnoreCase("Galaxy nexus"))
+			mAudiomanager_mode_internal = AudioManager.MODE_IN_CALL;
+
+		if (mAudiomanager_mode_internal == AudioManager.MODE_NORMAL)
+			Log.i("AudioCompatibility", "MODE for earpiece mode: configure to use AudioManager.MODE_NORMAL");
+		else if (mAudiomanager_mode_internal == AudioManager.MODE_IN_CALL)
+			Log.i("AudioCompatibility", "MODE for earpiece mode: configure to use AudioManager.MODE_IN_CALL");
+		else if (mAudiomanager_mode_internal == AudioManager.MODE_IN_COMMUNICATION)
+			Log.i("AudioCompatibility", "MODE for earpiece mode: configure to use AudioManager.MODE_IN_COMMUNICATION");
+		else if (mAudiomanager_mode_internal == 4)
+			Log.i("AudioCompatibility", "MODE for earpiece mode: configure to use AudioManager.4");
+
+    	if (mAudiomanager_wa_galaxyS == AudioManager.MODE_IN_CALL)
+    		Log.i("AudioCompatibility", "MODE for earpiece mode: AudioManager.MODE_IN_CALL hack enabled!");
+    	else if (mAudiomanager_wa_galaxyS == AudioManager.MODE_NORMAL)
+    		Log.i("AudioCompatibility", "MODE for earpiece mode: AudioManager.MODE_NORMAL hack enabled!");
+
+    	configureAudioManagerMode_speakermode(context);
+    }
+
+    static private void configureAudioManagerMode_speakermode(Context context)
+    {
+		mAudiomanager_mode_speakermode = mAudiomanager_mode_internal;
+
+        if (Build.MODEL.equalsIgnoreCase("Galaxy nexus"))
+			mAudiomanager_mode_speakermode = AudioManager.MODE_IN_COMMUNICATION;
+
+		if (mAudiomanager_mode_speakermode == AudioManager.MODE_NORMAL)
+			Log.i("AudioCompatibility", "MODE for speakermode: configure to use AudioManager.MODE_NORMAL");
+		else if (mAudiomanager_mode_speakermode == AudioManager.MODE_IN_CALL)
+			Log.i("AudioCompatibility", "MODE for speakermode: configure to use AudioManager.MODE_IN_CALL");
+		else if (mAudiomanager_mode_speakermode == AudioManager.MODE_IN_COMMUNICATION)
+			Log.i("AudioCompatibility", "MODE for speakermode: configure to use AudioManager.MODE_IN_COMMUNICATION");
+		else if (mAudiomanager_mode_speakermode == 4)
+			Log.i("AudioCompatibility", "MODE for speakermode: configure to use AudioManager.4");
+
+    	if (mAudiomanager_wa_galaxyS == AudioManager.MODE_IN_CALL)
+    		Log.i("AudioCompatibility", "MODE for speakermode: AudioManager.MODE_IN_CALL hack enabled!");
+    	else if (mAudiomanager_wa_galaxyS == AudioManager.MODE_NORMAL)
+    		Log.i("AudioCompatibility", "MODE for speakermode: AudioManager.MODE_NORMAL hack enabled!");
+    }
+
+    static public void configureAudioManagerStreamType()
+    {
+    	if (Build.BRAND.toUpperCase(Locale.US).startsWith("ARCHOS")) {
+    		mAudiomanager_stream_type = AudioManager.STREAM_MUSIC;
+    	}
+    	if (Build.MODEL.toUpperCase(Locale.US).startsWith("ST8002")) {
+    		mAudiomanager_stream_type = AudioManager.STREAM_MUSIC;
+    	}
+    	if (Build.MODEL.toUpperCase(Locale.US).startsWith("SAMBM8001ND")) {
+    		mAudiomanager_stream_type = AudioManager.STREAM_MUSIC;
+    	}
+
+		if (mAudiomanager_stream_type == AudioManager.STREAM_MUSIC)
+			Log.i("AudioCompatibility", "configure to use AudioManager.STREAM_MUSIC");
+		else if (mAudiomanager_stream_type == AudioManager.STREAM_VOICE_CALL)
+			Log.i("AudioCompatibility", "configure to use AudioManager.STREAM_VOICE_CALL");
+    }
+
+    static public void configureAudioRecordAudioSource()
+    {
+    	//API 11
+    	if (Build.VERSION.SDK_INT>=11)
+    		mAudiomanager_audio_source_internal = MediaRecorder.AudioSource.VOICE_COMMUNICATION;
+    	else
+    		mAudiomanager_audio_source_internal = MediaRecorder.AudioSource.MIC;
+
+    	if (Build.MODEL.equalsIgnoreCase("NEXUS S")) {
+    		mAudiomanager_audio_source_internal = MediaRecorder.AudioSource.VOICE_COMMUNICATION;
+    	}
+
+    	if (Build.MODEL.equalsIgnoreCase("MZ604")) {
+    		mAudiomanager_audio_source_internal = MediaRecorder.AudioSource.MIC;
+    	}
+
+    	//fix for Galaxy Nexus: using VOICE_COMMUNICATION -> "no sound mainly when coming back from speakermode"
+    	if (Build.MODEL.equalsIgnoreCase("Galaxy Nexus")) {
+    		mAudiomanager_audio_source_speakermode = MediaRecorder.AudioSource.MIC;
+    	}
+
+		if (mAudiomanager_audio_source_internal == MediaRecorder.AudioSource.DEFAULT)
+			Log.i("AudioCompatibility", "configure to use MediaRecorder.AudioSource.DEFAULT");
+		else if (mAudiomanager_audio_source_internal == MediaRecorder.AudioSource.MIC)
+			Log.i("AudioCompatibility", "configure to use MediaRecorder.AudioSource.MIC");
+		else if (mAudiomanager_audio_source_internal == MediaRecorder.AudioSource.VOICE_UPLINK)
+			Log.i("AudioCompatibility", "configure to use MediaRecorder.AudioSource.VOICE_UPLINK");
+		else if (mAudiomanager_audio_source_internal == MediaRecorder.AudioSource.VOICE_DOWNLINK)
+			Log.i("AudioCompatibility", "configure to use MediaRecorder.AudioSource.VOICE_DOWNLINK");
+		else if (mAudiomanager_audio_source_internal == MediaRecorder.AudioSource.VOICE_CALL) //uplink + downlink
+			Log.i("AudioCompatibility", "configure to use MediaRecorder.AudioSource.VOICE_CALL");
+		else if (mAudiomanager_audio_source_internal == MediaRecorder.AudioSource.CAMCORDER)
+			Log.i("AudioCompatibility", "configure to use MediaRecorder.AudioSource.CAMCORDER");
+		else if (mAudiomanager_audio_source_internal == MediaRecorder.AudioSource.VOICE_RECOGNITION)
+			Log.i("AudioCompatibility", "configure to use MediaRecorder.AudioSource.VOICE_RECOGNITION");
+		else if (mAudiomanager_audio_source_internal == MediaRecorder.AudioSource.VOICE_COMMUNICATION)
+			Log.i("AudioCompatibility", "configure to use MediaRecorder.AudioSource.VOICE_COMMUNICATION");
+
+		configureAudioRecordAudioSource_speakermode();
+    }
+
+    static private void configureAudioRecordAudioSource_speakermode()
+    {
+    	if (Build.VERSION.SDK_INT>=11)
+    		mAudiomanager_audio_source_speakermode = MediaRecorder.AudioSource.VOICE_COMMUNICATION;
+    	else
+    		mAudiomanager_audio_source_speakermode = MediaRecorder.AudioSource.MIC;
+
+    	if (Build.MODEL.equalsIgnoreCase("NEXUS S")) {
+    		mAudiomanager_audio_source_speakermode = MediaRecorder.AudioSource.VOICE_COMMUNICATION;
+    	}
+
+    	if (Build.MODEL.equalsIgnoreCase("MZ604")) {
+    		mAudiomanager_audio_source_speakermode = MediaRecorder.AudioSource.MIC;
+    	}
+
+    	//fix for Galaxy Nexus: using VOICE_COMMUNICATION -> "no sound mainly when coming back from speakermode"
+    	if (Build.MODEL.equalsIgnoreCase("Galaxy Nexus")) {
+    		mAudiomanager_audio_source_speakermode = MediaRecorder.AudioSource.MIC;
+    	}
+
+		if (mAudiomanager_audio_source_speakermode == MediaRecorder.AudioSource.DEFAULT)
+			Log.i("AudioCompatibility", "MIC for speakermode: configure to use MediaRecorder.AudioSource.DEFAULT");
+		else if (mAudiomanager_audio_source_speakermode == MediaRecorder.AudioSource.MIC)
+			Log.i("AudioCompatibility", "MIC for speakermode: configure to use MediaRecorder.AudioSource.MIC");
+		else if (mAudiomanager_audio_source_speakermode == MediaRecorder.AudioSource.VOICE_UPLINK)
+			Log.i("AudioCompatibility", "MIC for speakermode: configure to use MediaRecorder.AudioSource.VOICE_UPLINK");
+		else if (mAudiomanager_audio_source_speakermode == MediaRecorder.AudioSource.VOICE_DOWNLINK)
+			Log.i("AudioCompatibility", "MIC for speakermode: configure to use MediaRecorder.AudioSource.VOICE_DOWNLINK");
+		else if (mAudiomanager_audio_source_speakermode == MediaRecorder.AudioSource.VOICE_CALL) //uplink + downlink
+			Log.i("AudioCompatibility", "MIC for speakermode: configure to use MediaRecorder.AudioSource.VOICE_CALL");
+		else if (mAudiomanager_audio_source_speakermode == MediaRecorder.AudioSource.CAMCORDER)
+			Log.i("AudioCompatibility", "MIC for speakermode: configure to use MediaRecorder.AudioSource.CAMCORDER");
+		else if (mAudiomanager_audio_source_speakermode == MediaRecorder.AudioSource.VOICE_RECOGNITION)
+			Log.i("AudioCompatibility", "MIC for speakermode: configure to use MediaRecorder.AudioSource.VOICE_RECOGNITION");
+		else if (mAudiomanager_audio_source_speakermode == MediaRecorder.AudioSource.VOICE_COMMUNICATION)
+			Log.i("AudioCompatibility", "MIC for speakermode: configure to use MediaRecorder.AudioSource.VOICE_COMMUNICATION");
+    }
+
+    static public int getRate(int mRate)
+    {
+		if (Build.DEVICE.toUpperCase(Locale.US).startsWith("GT-P1010"))
+    		return 44100;
+		return mRate;
+    }
+
+    static public int getAudioRecord_getMinBufferSize(int bufsize)
+    {
+		if (Build.DEVICE.toUpperCase(Locale.US).startsWith("GT-P1010"))
+    		return bufsize*3;
+		return bufsize;
+    }    
+}

+ 307 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/AudioInput.java

@@ -0,0 +1,307 @@
+/*
+  vvphone is a SIP app for android.
+  vvsip is a SIP library for softphone (SIP -rfc3261-)
+  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+ */
+
+package com.vvsip.ansip;
+
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.MediaRecorder;
+import android.os.Build;
+import android.util.Log;
+
+//import android.media.audiofx.AcousticEchoCanceler;
+//import android.media.audiofx.AutomaticGainControl;
+
+/*
+ * 语音输入类,功能:采集语音
+ */
+public class AudioInput {
+
+	public static boolean beready=false;
+	public static boolean restart = false;
+	public static int mAudiomanager_audio_source = MediaRecorder.AudioSource.MIC;
+	public static boolean muted = false;
+
+	AudioRecord record;
+	// AutomaticGainControl agc;
+	// AcousticEchoCanceler aec;
+	boolean running = false;
+	int offset = 0;
+	int mRate = 8000;//20160617.8000->16000
+
+	AudioInput(int rate) {
+
+	//	rate = 8000;//20160617.add line.
+		
+		mRate = rate;
+		
+		if (beready==false)
+			return;
+			
+		int bufsize = AudioRecord.getMinBufferSize(rate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
+		if (bufsize < 2048)
+			bufsize = 2048;
+		bufsize = AudioCompatibility.getAudioRecord_getMinBufferSize(bufsize);
+		Log.i("AudioInput", "AudioRecord will use buffer = " + bufsize);
+		Log.i("AudioInput", "AudioRecord will use rate = " + rate);
+
+		android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
+
+		record = new AudioRecord(mAudiomanager_audio_source, rate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufsize);
+		if (record != null) {
+			// if (Build.VERSION.SDK_INT>=16)
+			// {
+			// try {
+			//
+			// if (AutomaticGainControl.isAvailable()) {
+			// int session_id = record.getAudioSessionId();
+			// agc = AutomaticGainControl.create(session_id);
+			// Log.w("AudioInput",
+			// "AudioRecord with AGC created / session id = " + session_id);
+			// if (agc.getEnabled()==false) {
+			// Log.w("AudioInput", "AudioRecord with AGC not yet enabled");
+			// agc.setEnabled(true);
+			// }
+			// if (agc.getEnabled()==true) {
+			// Log.w("AudioInput", "AudioRecord with AGC enabled");
+			// }
+			// }
+			// } catch (Exception e) {
+			// }
+			// try {
+			// if (AcousticEchoCanceler.isAvailable()) {
+			// int session_id = record.getAudioSessionId();
+			// aec = AcousticEchoCanceler.create(session_id);
+			// Log.w("AudioInput",
+			// "AudioRecord with AEC created / session id = " + session_id);
+			// if (aec!=null) {
+			// if (aec.getEnabled()==false) {
+			// Log.w("AudioInput", "AudioRecord with AEC not yet enabled");
+			// aec.setEnabled(true);
+			// }
+			// if (aec.getEnabled()==true) {
+			// Log.w("AudioInput",
+			// "AudioRecord with AEC enabled and hasControl = " +
+			// aec.hasControl());
+			// }
+			// }
+			// }
+			// } catch (Exception e) {
+			// }
+			// }
+			if (mAudiomanager_audio_source != record.getAudioSource()) {
+				Log.w("AudioInput", "AudioRecord is using " + record.getAudioSource() + " audio source instead of "
+						+ mAudiomanager_audio_source);
+			}
+		}
+		if (record == null) {
+			/* ressource not yet available? */
+			Log.w("AudioInput", "AudioRecord not yet available, retry later");
+			running = false;
+		} else {
+			running = true;
+			try {
+				record.startRecording();
+			} catch (Exception e) {
+				try {
+					record.stop();
+				} catch (Exception e1) {
+
+				}
+				record.release();
+				record = null;
+				// if (Build.VERSION.SDK_INT>=16) {
+				// if (agc!=null)
+				// agc.release();
+				// agc=null;
+				// if (aec!=null)
+				// aec.release();
+				// aec=null;
+				// }
+				running = false;
+			}
+		}
+	}
+
+	/** Stops running */
+	public int stop() {
+		if (record != null) {
+			running = false;
+			record.stop();
+			record.release();
+			record = null;
+			// if (Build.VERSION.SDK_INT>=16) {
+			// if (agc!=null)
+			// agc.release();
+			// agc=null;
+			// if (aec!=null)
+			// aec.release();
+			// aec=null;
+			// }
+		}
+		return 0;
+	}
+
+	int read_bytes(byte[] data, int len) {
+
+		if (beready==false) {
+			if (record != null) {
+				Log.i("AudioInput", "AudioRecord is now unused: stop");
+				running = false;
+				record.stop();
+				record.release();
+				record = null;
+				restart = false;
+				// if (Build.VERSION.SDK_INT>=16) {
+				// if (agc!=null)
+				// agc.release();
+				// agc=null;
+				// if (aec!=null)
+				// aec.release();
+				// aec=null;
+				// }
+			}
+			//Log.i("AudioInput", "AudioRecord is now unused: stopped");
+			
+			return 0;
+		}
+		
+		if (restart == true && Build.VERSION.SDK_INT >= 14) {
+			// let's consider restart is not required anymore for 4.0 and above.
+			restart = false;
+		}
+		if (record == null || restart == true) {
+			if (record != null) {
+				running = false;
+				record.stop();
+				record.release();
+				record = null;
+				// if (Build.VERSION.SDK_INT>=16) {
+				// if (agc!=null)
+				// agc.release();
+				// agc=null;
+				// if (aec!=null)
+				// aec.release();
+				// aec=null;
+				// }
+			}
+
+			int bufsize = AudioRecord.getMinBufferSize(mRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
+			if (bufsize < 2048)
+				bufsize = 2048;
+			bufsize = AudioCompatibility.getAudioRecord_getMinBufferSize(bufsize);
+			Log.i("AudioInput", "AudioRecord will use buffer = " + bufsize);
+
+			record = new AudioRecord(mAudiomanager_audio_source, mRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,
+					bufsize);
+			if (record != null) {
+				restart = false;
+				// if (Build.VERSION.SDK_INT>=16)
+				// {
+				// try {
+				// if (AutomaticGainControl.isAvailable()) {
+				// int session_id = record.getAudioSessionId();
+				// agc = AutomaticGainControl.create(session_id);
+				// if (agc!=null) {
+				// Log.w("AudioInput",
+				// "AudioRecord with AGC created / session id = " + session_id);
+				// if (agc.getEnabled()==false) {
+				// Log.w("AudioInput", "AudioRecord with AGC not yet enabled");
+				// agc.setEnabled(true);
+				// }
+				// if (agc.getEnabled()==true) {
+				// Log.w("AudioInput", "AudioRecord with AGC enabled");
+				// }
+				// }
+				// }
+				// } catch (Exception e) {
+				// }
+				// try {
+				// if (AcousticEchoCanceler.isAvailable()) {
+				// int session_id = record.getAudioSessionId();
+				// aec = AcousticEchoCanceler.create(session_id);
+				// Log.w("AudioInput",
+				// "AudioRecord with AEC created / session id = " + session_id);
+				// if (aec!=null) {
+				// if (aec.getEnabled()==false) {
+				// Log.w("AudioInput", "AudioRecord with AEC not yet enabled");
+				// aec.setEnabled(true);
+				// }
+				// if (aec.getEnabled()==true) {
+				// Log.w("AudioInput",
+				// "AudioRecord with AEC enabled and hasControl = " +
+				// aec.hasControl());
+				// }
+				// }
+				// }
+				// } catch (Exception e) {
+				// }
+				// }
+				if (mAudiomanager_audio_source != record.getAudioSource()) {
+					Log.w("AudioInput", "AudioRecord is using " + record.getAudioSource() + " audio source instead of "
+							+ mAudiomanager_audio_source);
+				}
+			}
+			if (record == null) {
+				/* resource not yet available? */
+				running = false;
+			} else {
+				Log.w("AudioInput", "AudioRecord finally available!");
+				running = true;
+
+				try {
+					record.startRecording();
+				} catch (Exception e) {
+					try {
+						record.stop();
+					} catch (Exception e1) {
+
+					}
+					record.release();
+					record = null;
+					// if (Build.VERSION.SDK_INT>=16) {
+					// if (agc!=null)
+					// agc.release();
+					// agc=null;
+					// if (aec!=null)
+					// aec.release();
+					// aec=null;
+					// }
+					running = false;
+				}
+			}
+		}
+
+		if (record != null) {
+			int num = record.read(data, 0, len);
+			
+			if (num== AudioRecord.ERROR_INVALID_OPERATION) {
+				try {
+					record.stop();
+				} catch (Exception e1) {
+	
+				}
+				record.release();
+				record = null;
+				running = false;
+			}
+			
+			if (muted == true) {
+				for (int k = 0; k < len; k++) {
+					data[k] = 0;
+				}
+			}
+			
+			offset += num;
+			if (num < len) {
+				Log.w("AudioInput", "AudioRecord return smaller data than expected!" + num + " asked:" + len);
+			}
+			return num;
+		}
+
+		return 0;
+	}
+}

+ 157 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/AudioOutput.java

@@ -0,0 +1,157 @@
+/*
+  vvphone is a SIP app for android.
+  vvsip is a SIP library for softphone (SIP -rfc3261-)
+  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+ */
+
+package com.vvsip.ansip;
+
+import android.media.AudioFormat;
+import android.media.AudioTrack;
+import android.os.Build;
+import android.util.Log;
+
+/*
+ * 语音输出类,功能:播放语音
+ */
+public class AudioOutput {
+
+	public static boolean beready = false;
+	public static boolean restart = false;
+
+	public static final int BUFFER_SIZE = 640;
+
+	AudioTrack track;
+	boolean running = false;
+	// short[] lin;
+	int offset;
+	int mRate = 8000;
+
+	AudioOutput(int rate) {
+
+		mRate = rate;
+
+		if (beready == false)
+			return;
+
+		running = true;
+
+		int bufsize = AudioTrack.getMinBufferSize(rate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
+
+		int c = bufsize / BUFFER_SIZE;
+		bufsize = BUFFER_SIZE * c + BUFFER_SIZE;
+		if (bufsize < BUFFER_SIZE * 2 * 2)
+			bufsize = BUFFER_SIZE * 2 * 2;
+		android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
+		track = new AudioTrack(AudioCompatibility.mAudiomanager_stream_type, rate, AudioFormat.CHANNEL_OUT_MONO,
+				AudioFormat.ENCODING_PCM_16BIT, bufsize, AudioTrack.MODE_STREAM);
+		Log.i("AudioOutput", "new AudioTrack created!");
+		track.setStereoVolume(AudioTrack.getMaxVolume(), AudioTrack.getMaxVolume());
+
+		offset = 0;
+
+		try {
+			track.play();
+		} catch (Exception e) {
+			track.flush();
+			track.release();
+			track = null;
+		}
+	}
+
+	/** Stops running */
+	public int stop() {
+		running = false;
+		if (track == null)
+			return 0;
+
+		track.flush();
+		track.stop();
+		track.release();
+		track = null;
+		return 0;
+	}
+
+	public int write_bytes(byte[] data, int len) {
+		int size;
+		if (len > BUFFER_SIZE)
+			return 0;
+
+		if (beready == false) {
+			restart = false;
+			if (track != null) {
+				Log.i("AudioOutput", "AudioTrack is now unused: stop");
+				track.flush();
+				track.stop();
+				track.release();
+				track = null;
+			}
+			return 0;
+		}
+
+		if (restart == true && Build.VERSION.SDK_INT >= 14) {
+			// let's consider restart is not required anymore for 4.0 and above.
+			restart = false;
+		}
+		if (track == null || restart == true) {
+			if (AudioInput.restart == true) {
+				/* wait for INPUT to be ready... */
+				return 0;
+			}
+			restart = false;
+			if (track != null) {
+				track.flush();
+				track.stop();
+				track.release();
+				track = null;
+			}
+
+			int bufsize = AudioTrack.getMinBufferSize(mRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
+
+			int c = bufsize / BUFFER_SIZE;
+			bufsize = BUFFER_SIZE * c + BUFFER_SIZE;
+			if (bufsize < BUFFER_SIZE * 2 * 2)
+				bufsize = BUFFER_SIZE * 2 * 2;
+			android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
+			track = new AudioTrack(AudioCompatibility.mAudiomanager_stream_type, mRate, AudioFormat.CHANNEL_OUT_MONO,
+					AudioFormat.ENCODING_PCM_16BIT, bufsize, AudioTrack.MODE_STREAM);
+			Log.i("AudioOutput", "new(2) AudioTrack created!");
+			track.setStereoVolume(AudioTrack.getMaxVolume(), AudioTrack.getMaxVolume());
+			offset = 0;
+
+			try {
+				track.play();
+			} catch (Exception e) {
+				track.flush();
+				track.release();
+				track = null;
+				return len;
+			}
+		}
+
+		if (len == 0)
+			return 0; /*
+					 * len=0 is asked in order to allow beready to be checked,
+					 * in order to prepare the audiotrack before the call is
+					 * established
+					 */
+
+		int diff = 0;
+		try {
+			diff = offset - track.getPlaybackHeadPosition();
+		} catch (Exception e) {
+			// might be an IllegalStateException? happened once on galaxy S.
+			return len;
+		}
+		if (diff < 1024) {
+			Log.w("VvsipService", "inserting silence//diff = " + diff + " samples");
+			byte silence[] = new byte[BUFFER_SIZE];
+			track.write(silence, 0, BUFFER_SIZE);
+			offset += BUFFER_SIZE;
+		}
+		size = track.write(data, 0, len);
+		offset += size;
+		return size;
+	}
+
+}

+ 33 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/CheckCpu.java

@@ -0,0 +1,33 @@
+/*
+  vvphone is a SIP app for android.
+  vvsip is a SIP library for softphone (SIP -rfc3261-)
+  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+*/
+
+package com.vvsip.ansip;
+
+import android.util.Log;
+
+/*
+ * 检测CPU接口,增强系统兼容性
+ */
+public class CheckCpu {
+
+
+	public native int getcpufamily();
+	public native int getcpufeatures();
+
+	/* 
+	 */
+	static {
+		try {
+			System.loadLibrary("checkcpu");
+		} catch (UnsatisfiedLinkError e) {
+			Log.e("VvsipTask", "native library is missing // re-install the application...");
+			e.printStackTrace();
+		} catch (Exception e) {
+			Log.e("VvsipTask", "problem loading checkcpu.so?");
+			e.printStackTrace();
+		}
+	}	
+}

+ 392 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/H264MediaCodecDecoder.java

@@ -0,0 +1,392 @@
+/*
+  vvphone is a SIP app for android.
+  vvsip is a SIP library for softphone (SIP -rfc3261-)
+  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+*/
+
+package com.vvsip.ansip;
+
+import android.annotation.TargetApi;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+import android.os.Build;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.List;
+
+class OmxFormatInfo {
+	int omx_format;
+	int mediastreamer2_format;
+	int size_mul;
+	int line_mul;
+	int line_chroma_div;
+	
+	OmxFormatInfo(int _omx_format, int _mediastreamer2_format, int _size_mul, int _line_mul, int _line_chroma_div) {
+		omx_format=_omx_format;
+		mediastreamer2_format=_mediastreamer2_format;
+		size_mul=_size_mul;
+		line_mul=_line_mul;
+		line_chroma_div=_line_chroma_div;
+	}
+};
+
+
+/*
+ * H264硬解码类
+ */
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+public class H264MediaCodecDecoder  {
+
+	static final String mTag = "H264MediaCodecDecoder";
+	
+	MediaCodec mediaCodec;
+	MediaCodec.BufferInfo bufferInfo;
+    int ppssps_done;
+    int width;
+	int height;
+    int stride;
+    int slice_height;
+    int color_format;
+    int crop_left;
+    int crop_top;
+    int crop_right;
+    int crop_bottom;
+
+    public int getWidth() {
+		return width;
+	}
+
+	public int getHeight() {
+		return height;
+	}
+
+	public int getStride() {
+		return stride;
+	}
+
+	public int getSliceHeight() {
+		return slice_height;
+	}
+
+	public int getColorFormat() {
+		return color_format;
+	}
+
+	public int getCropLeft() {
+		return crop_left;
+	}
+
+	public int getCropTop() {
+		return crop_top;
+	}
+
+	public int getCropRight() {
+		return crop_right;
+	}
+
+	public int getCropBottom() {
+		return crop_bottom;
+	}
+    
+    
+	public static VvsipMediaCodecInfo mMediaCodecInfo = null;
+	
+	//static public int __get_ms_format() {
+	//	return mMediaCodecInfo.mMSColorFormat;
+	//}
+
+	public int getMSColorFormat() {
+		int ms_format = -1;
+		if (color_format==mMediaCodecInfo.mColorFormat)
+			return mMediaCodecInfo.mMSColorFormat;
+		
+		if (color_format==MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar)
+	        ms_format = 0; //MS_YUV420P;
+	      else if (color_format==MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar)
+	    	  ms_format = 0; //MS_YUV420P;
+	      else if (color_format==MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar)
+	    	  ms_format = 9; //MS_NV12;
+	      else if (color_format==0x7f000001) /* MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanarInterlaced */
+	    	  ms_format = 9; //MS_NV12;
+	      else if (color_format==MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar)
+	    	  ms_format = 9; //MS_NV12;
+	      else if (color_format==2141391875) /* MediaCodecInfo.CodecCapabilities.QOMX_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka (supposed to be NV12MT?) */
+	        /* might be usefull?: */
+	        /* https://github.com/Owersun/xbmc/blob/8a2f587242eaedc03b8a0dfb0000eee3d0522db7/xbmc/cores/dvdplayer/DVDCodecs/Video/nv12mt_to_yuv420m.neon.S */
+	    	  ms_format = 9; //MS_NV12;
+	      else if (color_format==2141391876) /* MediaCodecInfo.CodecCapabilities.OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m (detected on nexus 5) */
+	    	  ms_format = 9; //MS_NV12;
+	      else if (color_format==MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar) /*  */
+	    	  ms_format = 9; //MS_NV12;
+	      else if (color_format==842094169) /* YV12 (U and V reversed) */
+	    	  ms_format = 0; //MS_YUV420P;
+
+		return ms_format;
+	}
+	
+
+	private static Hashtable<Integer, OmxFormatInfo> omxFormatInfoList = new Hashtable<Integer, OmxFormatInfo>();
+
+	static {
+		Integer key;
+		OmxFormatInfo val;
+
+		key = Integer.valueOf(MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
+		val = new OmxFormatInfo(MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar, 0, 3, 1, 2);
+		omxFormatInfoList.put(key, val);
+		key = Integer.valueOf(MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar);
+		val = new OmxFormatInfo(MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar, 0, 3, 1, 2);
+		omxFormatInfoList.put(key, val);
+		key = Integer.valueOf(MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar); //NV21 detected on samsung S3?
+		val = new OmxFormatInfo(MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar, 8 , 3, 1, 1);
+		omxFormatInfoList.put(key, val);
+		key = Integer.valueOf(MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar); //NV21
+		val = new OmxFormatInfo(MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar, 8, 3, 1, 1);
+		omxFormatInfoList.put(key, val);
+		key = Integer.valueOf(MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar); //NV12
+		val = new OmxFormatInfo(MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar, 9, 3, 1, 2);
+		omxFormatInfoList.put(key, val);
+		key = Integer.valueOf(0x7FA30C03); //NV12 //OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka
+		val = new OmxFormatInfo(0x7FA30C03, 0, 3, 1, 1);
+		omxFormatInfoList.put(key, val);
+		key = Integer.valueOf(MediaCodecInfo.CodecCapabilities.COLOR_FormatYCbYCr); //YUYV
+		val = new OmxFormatInfo(MediaCodecInfo.CodecCapabilities.COLOR_FormatYCbYCr, 1, 4, 2, 0);
+		omxFormatInfoList.put(key, val);
+		key = Integer.valueOf(MediaCodecInfo.CodecCapabilities.COLOR_FormatCbYCrY); //UYVY
+		val = new OmxFormatInfo(MediaCodecInfo.CodecCapabilities.COLOR_FormatCbYCrY, 5, 4, 2, 0);
+		omxFormatInfoList.put(key, val);
+    }
+
+	H264MediaCodecDecoder() {
+		
+		
+		mediaCodec = null;
+		bufferInfo = null;
+	}
+	
+	public void start() {
+		Log.d(mTag, "H264MediaCodecDecoder: start");
+		ppssps_done=0;
+		try {
+			Log.e(mTag, "INFO_OUTPUT_FORMAT_CHANGED: codec name: " + mMediaCodecInfo.info.getName());
+		    bufferInfo = new MediaCodec.BufferInfo();
+			mediaCodec = MediaCodec.createByCodecName(mMediaCodecInfo.info.getName());
+			MediaFormat mediaFormat = MediaFormat.createVideoFormat(mMediaCodecInfo.mMimeType, 352, 288);
+		    mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, mMediaCodecInfo.mColorFormat);
+		    //mediaFormat.setByteBuffer("csd-0", bytes);
+		    mediaCodec.configure(mediaFormat, null, null, 0);
+		    mediaCodec.start();
+		} catch (Exception e) {
+	        e.printStackTrace();
+	        mediaCodec=null;
+	        bufferInfo=null;
+		}
+	}
+	
+	public int close() {
+		if (mediaCodec==null)
+			return 0;
+	    try {
+	        mediaCodec.stop();
+	        mediaCodec.release();
+	    } catch (Exception e){
+	        e.printStackTrace();
+	    }
+        mediaCodec=null;
+        bufferInfo=null;
+        return 0;
+	}
+
+	
+	public int decode_data(int flag, byte[] input, int input_len) {
+		
+		//Log.d(mTag, "H264MediaCodecDecoder: decode_data");
+		if (mediaCodec==null || bufferInfo==null) {
+			start();
+		}
+		if (mediaCodec==null || bufferInfo==null) {
+			return -1;
+		}
+		if (ppssps_done==0 && flag!=2) {
+            Log.i(mTag, "H264MediaCodecDecoder: missing sps/pps");
+			return -2;
+		}
+		
+	    try {
+	    	int inputBufferIndex;
+	        try {
+	        	inputBufferIndex = mediaCodec.dequeueInputBuffer(5000);
+	        }catch (Exception e) {
+	        	Log.e(mTag, "H264MediaCodecDecoder: dequeueInputBuffer" + e.getMessage());
+	        	close();
+	        	return -1;
+			} 
+	        
+	        if (inputBufferIndex >= 0) {
+	            ByteBuffer inputBuffer;
+	        	if (Build.VERSION.SDK_INT>=21)
+	        		inputBuffer = mediaCodec.getInputBuffer(inputBufferIndex);
+	        	else {
+	    	        ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
+	        		inputBuffer = inputBuffers[inputBufferIndex];
+	        	}
+	            inputBuffer.clear();
+	            inputBuffer.put(input);
+	            mediaCodec.queueInputBuffer(inputBufferIndex, 0, input_len, 0, flag);
+	    		if (flag==2)
+	    			ppssps_done=1;
+	        } else {
+	            //Log.i(mTag, "H264MediaCodecDecoder: no input buffer");
+	            return -3;
+	        }
+
+	    } catch (Exception e) {
+	        e.printStackTrace();
+		    return -1;
+	    }
+
+	    //Log.d(mTag, "H264MediaCodecDecoder: end of decode_data");
+	    return 0;
+	}
+
+	//private static List<String> nopadding_decoders2 = new ArrayList<String>() {{ add("OMX.SEC.avc.dec"); add("OMX.SEC.avcdec"); add("OMX.SEC.MPEG4.Decoder"); add("OMX.SEC.vc1.dec"); }};
+	private static List<String> nopadding_decoders = Collections.unmodifiableList(Arrays.asList(new String[] {
+			"OMX.SEC.avc.dec",
+			"OMX.SEC.avcdec",
+			"OMX.SEC.MPEG4.Decoder",
+			"OMX.SEC.mpeg4.dec",
+			"OMX.SEC.vc1.dec"}));
+
+	public int get_raw_data(byte[] output, int max_output_len) {
+
+
+		int output_len=0;
+		//Log.d(mTag, "H264MediaCodecDecoder: get_h264_data");
+		if (mediaCodec==null || bufferInfo==null) {
+			return -1;
+		}
+
+	    try {
+	        ByteBuffer[] outputBuffers;
+
+
+	        int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,0);
+	        if (outputBufferIndex >= 0) {
+	        	ByteBuffer outputBuffer;
+	        	if (Build.VERSION.SDK_INT>=21)
+	        		outputBuffer = mediaCodec.getOutputBuffer(outputBufferIndex);
+	        	else {
+	    	        try {
+	    	        	outputBuffers = mediaCodec.getOutputBuffers();
+	    	        }catch (Exception e) {
+	    	        	Log.e(mTag, "H264MediaCodecDecoder: getOutputBuffers" + e.getMessage());
+	    	        	close();
+	    	        	return -1;
+	    			} 
+		            outputBuffer = outputBuffers[outputBufferIndex];
+	        		
+	        	}
+	            //byte[] outData = new byte[bufferInfo.size];
+	            if (outputBuffer.remaining()>max_output_len) {
+		            Log.i(mTag, "output buffer too small outputBuffer.remaining()=" + outputBuffer.remaining() + " bufferInfo.size=" + bufferInfo.size + " max=" + max_output_len);
+		            output_len = -outputBuffer.remaining(); //return new required allocation size
+	            } else {
+	            	//Log.i(mTag, "remaining bytes in outputBuffer=" + outputBuffer.remaining() + " bufferInfo.size=" + bufferInfo.size + " bufferInfo.offset=" + bufferInfo.offset);
+		            //output_len = bufferInfo.offset+bufferInfo.size;
+	            	//outputBuffer.get(output, 0, bufferInfo.offset+bufferInfo.size);
+		            output_len = outputBuffer.remaining();
+	            	outputBuffer.get(output, 0, outputBuffer.remaining());
+	            }
+
+	            mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
+	        } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+	            outputBuffers = mediaCodec.getOutputBuffers();
+	            return 0;
+	        } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+	            // Subsequent data will conform to new format.
+	            MediaFormat format = mediaCodec.getOutputFormat();
+	            
+	            width        = format.getInteger("width");
+	            height       = format.getInteger("height");
+	            stride       = format.getInteger("stride");
+	            slice_height = format.getInteger("slice-height");
+	            color_format = format.getInteger("color-format");
+	            crop_left    = format.getInteger("crop-left");
+	            crop_top     = format.getInteger("crop-top");
+	            crop_right   = format.getInteger("crop-right");
+	            crop_bottom  = format.getInteger("crop-bottom");
+	            
+	            Log.e(mTag, "INFO_OUTPUT_FORMAT_CHANGED: width: " + width);
+	            Log.e(mTag, "INFO_OUTPUT_FORMAT_CHANGED: height: " + height);
+	            Log.e(mTag, "INFO_OUTPUT_FORMAT_CHANGED: stride: " + stride);
+	            Log.e(mTag, "INFO_OUTPUT_FORMAT_CHANGED: slice-height: " + slice_height);
+	            Log.e(mTag, "INFO_OUTPUT_FORMAT_CHANGED: color-format: " + color_format);
+	            Log.e(mTag, "INFO_OUTPUT_FORMAT_CHANGED: crop-left: " + crop_left);
+	            Log.e(mTag, "INFO_OUTPUT_FORMAT_CHANGED: crop-top: " + crop_top);
+	            Log.e(mTag, "INFO_OUTPUT_FORMAT_CHANGED: crop-right: " + crop_right);
+	            Log.e(mTag, "INFO_OUTPUT_FORMAT_CHANGED: crop-bottom: " + crop_bottom);
+	            
+	            if (stride<=0)
+	            	stride=width;
+	            if (slice_height<=0)
+	            	slice_height=height;
+	            
+	            if (color_format == MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar) {
+	            	//ignore:
+	            	//slice_height -= crop_top/2;
+	            	//crop_top=0;
+	            	//crop_left=0;
+	            }
+	            int i_width=crop_right + 1 - crop_left;
+	            //int i_heigth=crop_bottom + 1 - crop_top;
+	            if (nopadding_decoders.contains(mMediaCodecInfo.info.getName())) {
+	            	slice_height=0; /* ? */
+	            	stride = i_width;
+	            }
+
+	            /* Align on macroblock boundary */
+	            //int aligned_width = (i_width + 15) & ~0xF;
+	            //int aligned_height = (i_heigth + 15) & ~0xF;
+	            
+	            
+	            OmxFormatInfo val = omxFormatInfoList.get(Integer.valueOf(color_format));
+	            if (val!=null) {
+		            //int size = aligned_width * aligned_height * val.size_mul / 2;
+		            //int pitch = aligned_width * aligned_height * val.line_mul;
+		            
+		            Log.d(mTag, "H264MediaCodecDecoder: =" + val.omx_format);
+		            Log.d(mTag, "H264MediaCodecDecoder: =" + val.size_mul);
+		            Log.d(mTag, "H264MediaCodecDecoder: =" + val.line_mul);
+		            Log.d(mTag, "H264MediaCodecDecoder: =" + val.line_chroma_div);
+	            }
+            	return -999;
+	        }
+	    } catch (Exception e) {
+	    	e.printStackTrace();
+		    return -1;
+	    }
+	    //Log.d(mTag, "H264MediaCodecDecoder: end of get_h264_data  output_len=" + output_len);
+	    return output_len;
+	}
+	
+	public static boolean TestDecoder() {
+		Log.d(mTag, "TestDecoder");
+		try {
+			MediaCodec mediaCodecTest = MediaCodec.createByCodecName(mMediaCodecInfo.info.getName());
+			MediaFormat mediaFormatTest = MediaFormat.createVideoFormat(mMediaCodecInfo.info.getName(), 320, 240);
+			mediaCodecTest.configure(mediaFormatTest, null, null, 0);
+			mediaCodecTest.start();
+			mediaCodecTest.stop();
+			mediaCodecTest.release();
+		} catch (Exception e) {
+	        e.printStackTrace();
+	        return false;
+		}
+		return true;
+	}
+}

+ 189 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/H264MediaCodecEncoder.java

@@ -0,0 +1,189 @@
+/*
+  vvphone is a SIP app for android.
+  vvsip is a SIP library for softphone (SIP -rfc3261-)
+  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+ */
+
+package com.vvsip.ansip;
+
+import android.annotation.TargetApi;
+import android.media.MediaCodec;
+import android.media.MediaFormat;
+import android.os.Build;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+
+/*
+ * H264硬解码类
+ */
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+public class H264MediaCodecEncoder {
+
+	static final String mTag = "H264MediaCodecEncoder";
+
+	MediaCodec mediaCodec;
+	MediaCodec.BufferInfo bufferInfo;
+	int width;
+	int height;
+	int fps;
+	int bitrate;
+
+	public static VvsipMediaCodecInfo mMediaCodecInfo = null;
+
+	static public int get_format() {
+		return mMediaCodecInfo.mColorFormat;
+	}
+
+	static public int get_ms_format() {
+		return mMediaCodecInfo.mMSColorFormat;
+	}
+
+	H264MediaCodecEncoder() {
+		mediaCodec = null;
+		bufferInfo = null;
+	}
+
+	public void start(int _width, int _height, int _fps, int _bitrate) {
+		Log.d(mTag, "H264MediaCodecEncoder: start format=" + mMediaCodecInfo.mColorFormat);
+		try {
+			bufferInfo = new MediaCodec.BufferInfo();
+			mediaCodec = MediaCodec.createByCodecName(mMediaCodecInfo.info.getName());
+			MediaFormat mediaFormat = MediaFormat.createVideoFormat(mMediaCodecInfo.mMimeType, _width, _height);
+			mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, _bitrate);
+			mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, _fps);
+			mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, mMediaCodecInfo.mColorFormat);
+			mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
+			mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+			mediaCodec.start();
+			width = _width;
+			height = _height;
+			fps = _fps;
+			bitrate = _bitrate;
+		} catch (Exception e) {
+			e.printStackTrace();
+			mediaCodec = null;
+			bufferInfo = null;
+		}
+	}
+
+	public int close() {
+		if (mediaCodec == null)
+			return 0;
+		try {
+			mediaCodec.stop();
+			mediaCodec.release();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		mediaCodec = null;
+		bufferInfo = null;
+		return 0;
+	}
+
+	public int encode_data(int _width, int _height, int _fps, int _bitrate, byte[] input, int input_len) {
+
+		long timeMs = System.currentTimeMillis();
+		if (width != _width || height != _height || fps != _fps || bitrate != _bitrate) {
+			close();
+		}
+		if (mediaCodec == null || bufferInfo == null) {
+			start(_width, _height, _fps, _bitrate);
+		}
+		if (mediaCodec == null || bufferInfo == null) {
+			return -1;
+		}
+
+		try {
+			int inputBufferIndex = mediaCodec.dequeueInputBuffer(0);
+			if (inputBufferIndex >= 0) {
+				ByteBuffer inputBuffer;
+				if (Build.VERSION.SDK_INT >= 21)
+					inputBuffer = mediaCodec.getInputBuffer(inputBufferIndex);
+				else {
+					ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
+					inputBuffer = inputBuffers[inputBufferIndex];
+				}
+				inputBuffer.clear();
+				inputBuffer.put(input);
+				mediaCodec.queueInputBuffer(inputBufferIndex, 0, input_len, timeMs * 1000, 0);
+			} else {
+				Log.i(mTag, "H264MediaCodecEncoder: no input buffer");
+			}
+
+		} catch (Exception e) {
+			e.printStackTrace();
+			return -1;
+		}
+
+		// Log.d(mTag, "H264MediaCodecEncoder: end of encode_data");
+		return 0;
+	}
+
+	public int get_h264_data(byte[] output, int max_output_len) {
+
+		int output_len = 0;
+		// Log.d(mTag, "H264MediaCodecEncoder: get_h264_data");
+		if (mediaCodec == null || bufferInfo == null) {
+			return -1;
+		}
+
+		try {
+			int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
+			if (outputBufferIndex >= 0) {
+				ByteBuffer outputBuffer;
+				if (Build.VERSION.SDK_INT >= 21)
+					outputBuffer = mediaCodec.getOutputBuffer(outputBufferIndex);
+				else {
+					ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
+					outputBuffer = outputBuffers[outputBufferIndex];
+				}
+				if (bufferInfo.size > max_output_len) {
+					Log.i(mTag, "output buffer too small bufferInfo.size=" + bufferInfo.size + " max=" + max_output_len);
+				} else {
+					try {
+						outputBuffer.get(output, 0, bufferInfo.size);
+						output_len = bufferInfo.size;
+					} catch (Exception ex) {
+						ex.printStackTrace();
+						mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
+						output_len = 0;
+					}
+
+					// Log.i(mTag, output_len + " bytes written");
+				}
+
+				mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+			return -1;
+		}
+
+		// Log.d(mTag,
+		// "H264MediaCodecEncoder: end of get_h264_data  output_len=" +
+		// output_len);
+		return output_len;
+	}
+
+	public static boolean TestEncoder() {
+		Log.d(mTag, "TestEncoder");
+		try {
+			MediaCodec mediaCodecTest = MediaCodec.createByCodecName(mMediaCodecInfo.info.getName());
+			MediaFormat mediaFormatTest = MediaFormat.createVideoFormat(mMediaCodecInfo.info.getName(), 320, 240);
+			mediaFormatTest.setInteger(MediaFormat.KEY_BIT_RATE, 256000);
+			mediaFormatTest.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
+			mediaFormatTest.setInteger(MediaFormat.KEY_COLOR_FORMAT, mMediaCodecInfo.mColorFormat);
+			mediaFormatTest.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
+			mediaCodecTest.configure(mediaFormatTest, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+			mediaCodecTest.start();
+			mediaCodecTest.stop();
+			mediaCodecTest.release();
+		} catch (Exception e) {
+			e.printStackTrace();
+			return false;
+		}
+		return true;
+	}
+
+}

+ 32 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/IVvsipService.java

@@ -0,0 +1,32 @@
+/*
+  vvphone is a SIP app for android.
+  vvsip is a SIP library for softphone (SIP -rfc3261-)
+  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+*/
+
+package com.vvsip.ansip;
+
+import android.os.Handler;
+
+/*
+ * 服务接口
+ */
+public interface IVvsipService {
+	public VvsipTask getVvsipTask();
+	public int StartVvsipLayer();
+	public int StopVvsipLayer();
+	public void initiateOutgoingCall(String target, String macStr);
+    public void addListener(IVvsipServiceListener listener);
+    public void removeListener(IVvsipServiceListener listener);
+	public void clearListener();
+    public void setAudioNormalMode();
+    public void setAudioInCallMode();
+    public void setSpeakerModeOff();
+    public void setSpeakerModeOn();
+	public void stopPlayer();
+	public void restartNetworkDetection();
+	public void setMessageHandler(Handler _mainActivityMessageHandler);
+	public void register(String domain, String username, String passwd);
+	public void setNeighbors();
+	public void sendDTMF(String dtmf);
+}

+ 18 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/IVvsipServiceListener.java

@@ -0,0 +1,18 @@
+/*
+  vvphone is a SIP app for android.
+  vvsip is a SIP library for softphone (SIP -rfc3261-)
+  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+*/
+
+package com.vvsip.ansip;
+
+
+/*
+ * 服务监听接口
+ */
+public interface IVvsipServiceListener {
+	public void onNewVvsipCallEvent(VvsipCall call);
+	public void onRemoveVvsipCallEvent(VvsipCall call);
+	public void onStatusVvsipCallEvent(VvsipCall call);
+	public void onRegistrationEvent(int rid, String remote_uri, int code, String reason);
+}

+ 240 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/VideoOrientationCompatibility.java

@@ -0,0 +1,240 @@
+package com.vvsip.ansip;
+
+import android.annotation.SuppressLint;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.pm.ActivityInfo;
+import android.text.TextUtils;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/*
+ * 视频方向适配类,功能:增强兼容性
+ */
+public class VideoOrientationCompatibility {
+
+	final private static String KEY_PORTRAIT = "key_rotate_portrait";
+	final private static String KEY_LANDSCAPE = "key_rotate_landscape";
+	final private static String KEY_REVERSE_LANDSCAPE = "key_rotate_reverse_landscape";
+	final private static String KEY_REVERSE_PORTRAIT = "key_rotate_reverse_portrait";
+	
+	final private static List<Integer> possibleDisplayOrientations = Arrays.asList(0, 90, 180, 270);
+	final private static List<Integer> possibleImageOrientations = Arrays.asList(0, 1, 2, 3);
+	
+	private SharedPreferences configuration;
+	
+	private class Preset {
+		public Preset(Integer self, Integer remote) {
+			this.self = self;
+			this.remote = remote;
+		}
+		public Integer self;
+		public Integer remote;
+		
+		public List<Integer> getStorable() {
+			List<Integer> storable = Arrays.asList(self, remote);
+			return storable;
+		}
+		
+		public void fromStorable(Integer[] values) {
+			this.self = values[0];
+			this.remote = values[1];
+		}
+	}
+	
+	private class Settings {
+		public Preset frontCamera;
+		public Preset rearCamera;
+		
+		public Settings() {
+			this.frontCamera = new Preset(0, 0);
+			this.rearCamera = new Preset(0, 0);
+		}
+		public Settings(Preset front, Preset rear) {
+			this.frontCamera = front;
+			this.rearCamera = rear;
+		}
+		
+		
+		public List<Integer> getStorable() {
+			List<Integer> storable = new LinkedList<Integer>(frontCamera.getStorable());
+			storable.addAll(rearCamera.getStorable());
+			return storable;
+		}
+		
+		public void fromStorable(Integer[] values) {
+			Integer[] front = {values[0], values[1]};
+			frontCamera.fromStorable(front);
+			
+			Integer[] rear = {values[2], values[3]};
+			rearCamera.fromStorable(rear);
+		}
+	}
+	
+	@SuppressLint("UseSparseArrays")
+	private Map<Integer, Settings> orientationPresets =
+		new HashMap<Integer, Settings>();
+	
+	public VideoOrientationCompatibility(SharedPreferences configuration) {
+		this.configuration = configuration;
+		init();
+	}
+	
+	private void init() {
+		
+		if (hasSettingsStored()) {
+			load();
+			return;
+		}
+		
+		Settings portrait;
+		Settings landscape;
+		Settings reverseLandscape;
+		Settings reversePortrait;
+		
+		if (android.os.Build.MODEL.toUpperCase(Locale.US).startsWith("XOOM")) {
+			portrait 			= new Settings(new Preset(270, 3), new Preset(270, 3));
+			landscape 			= new Settings(new Preset(0,   0), new Preset(0,   0));
+			reverseLandscape 	= new Settings(new Preset(180, 2), new Preset(180, 2));
+			reversePortrait 	= new Settings(new Preset(90,  1), new Preset(90,  1));
+		}
+		else if (android.os.Build.MODEL.toUpperCase(Locale.US).startsWith("A500")) {
+			portrait 			= new Settings(new Preset(270, 1), new Preset(270, 3));
+			landscape 			= new Settings(new Preset(0,   0), new Preset(0,   0));
+			reverseLandscape 	= new Settings(new Preset(180, 2), new Preset(180, 2));
+			reversePortrait 	= new Settings(new Preset(90,  3), new Preset(90,  1));
+		}
+		else { // Default, for instance for Samsung Galaxy Nexus
+			portrait 			= new Settings(new Preset(90,  3), new Preset(90,  1));
+			landscape 			= new Settings(new Preset(0,   0), new Preset(0,   0));
+			reverseLandscape 	= new Settings(new Preset(180, 2), new Preset(180, 2));
+			reversePortrait 	= new Settings(new Preset(270, 1), new Preset(270, 3));
+		}
+		
+		orientationPresets.put(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, portrait);
+		orientationPresets.put(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, landscape);
+		orientationPresets.put(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE, reverseLandscape);
+		orientationPresets.put(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT, reversePortrait);
+	}
+	
+	public Integer getDisplayRotation(Integer deviceOrientation, Boolean isFrontFacingCamera) {
+		return getPreset(deviceOrientation, isFrontFacingCamera).self;
+	}
+	
+	public Integer getImageRotation(Integer deviceOrientation, Boolean isFrontFacingCamera) {
+		return getPreset(deviceOrientation, isFrontFacingCamera).remote;
+	}
+	
+	public void setDisplayRotation(Integer deviceOrientation, Boolean isFrontFacingCamera, Integer displayOrientation) {
+		
+		if (!possibleDisplayOrientations.contains(displayOrientation))
+			return;
+		
+		Preset preset = getPresetOrNull(deviceOrientation, isFrontFacingCamera);
+		
+		if (preset != null) {
+			preset.self = displayOrientation;
+			setPreset(deviceOrientation, isFrontFacingCamera, preset);
+		}
+	}
+	
+	public void setImageRotation(Integer deviceOrientation, Boolean isFrontFacingCamera, Integer displayOrientation) {
+		
+		if (!possibleImageOrientations.contains(displayOrientation))
+			return;
+		
+		Preset preset = getPresetOrNull(deviceOrientation, isFrontFacingCamera);
+		
+		if (preset != null) {
+			preset.remote = displayOrientation;
+			setPreset(deviceOrientation, isFrontFacingCamera, preset);
+		}
+	}
+	
+	private Preset getPreset(Integer deviceOrientation, Boolean isFrontFacingCamera) {
+		Preset preset = getPresetOrNull(deviceOrientation, isFrontFacingCamera);
+		if (preset != null)
+			return preset;
+		// Return default 
+		return new Preset(0, 0);
+	}
+	
+	private Preset getPresetOrNull(Integer deviceOrientation, Boolean isFrontFacingCamera) {
+		Settings settings = orientationPresets.get(deviceOrientation);
+		
+		if (settings != null) {
+			if (isFrontFacingCamera)
+				return settings.frontCamera;
+			else 
+				return settings.rearCamera;
+		}
+		return null;
+	}
+	
+	private void setPreset(Integer deviceOrientation, Boolean isFrontFacingCamera, Preset preset) {
+		if (isFrontFacingCamera)
+			orientationPresets.get(deviceOrientation).frontCamera = preset;
+		else
+			orientationPresets.get(deviceOrientation).rearCamera = preset;
+		
+		store();
+	}
+	
+	private void store() {
+		String portrait = TextUtils.join(";", orientationPresets.get(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT).getStorable());
+		String landscape = TextUtils.join(";", orientationPresets.get(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE).getStorable());
+		String reverseLandscape = TextUtils.join(";", orientationPresets.get(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE).getStorable());
+		String reversePortrait = TextUtils.join(";", orientationPresets.get(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT).getStorable());
+		
+		Editor editor = configuration.edit();
+		editor.putString(KEY_PORTRAIT, portrait);
+		editor.putString(KEY_LANDSCAPE, landscape);
+		editor.putString(KEY_REVERSE_LANDSCAPE, reverseLandscape);
+		editor.putString(KEY_REVERSE_PORTRAIT, reversePortrait);
+		editor.commit();
+	}
+	
+	private boolean hasSettingsStored() {
+		return !configuration.getString(KEY_PORTRAIT, "NOSUCHSTRING").equals("NOSUCHSTRING");
+	}
+	
+	private void load() {
+		if (!hasSettingsStored())
+			return;
+		
+		Settings portrait = getSettingsFromString(configuration.getString(KEY_PORTRAIT, ""));
+		Settings landscape = getSettingsFromString(configuration.getString(KEY_LANDSCAPE, ""));
+		Settings reverseLandscape = getSettingsFromString(configuration.getString(KEY_REVERSE_LANDSCAPE, ""));
+		Settings reversePortrait = getSettingsFromString(configuration.getString(KEY_REVERSE_PORTRAIT, ""));
+		
+		orientationPresets.put(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, portrait);
+		orientationPresets.put(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, landscape);
+		orientationPresets.put(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE, reverseLandscape);
+		orientationPresets.put(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT, reversePortrait);
+	}
+	
+	private Settings getSettingsFromString(String str) {
+		String[] arr = TextUtils.split(str, ";");
+		
+		if (arr.length != 4)
+			return null;
+		
+		Integer[] iarr = new Integer[4];
+		
+		for (int i = 0; i < 4; i++) {
+			Integer val = Integer.parseInt(arr[i]);
+			if (val == null)
+				return null;
+			iarr[i] = val;
+		}
+		
+		Settings settings = new Settings();
+		settings.fromStorable(iarr);
+		return settings;
+	}
+}

+ 163 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/VvsipAccount.java

@@ -0,0 +1,163 @@
+/*
+  vvphone is a SIP app for android.
+  vvsip is a SIP library for softphone (SIP -rfc3261-)
+  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+*/
+
+package com.vvsip.ansip;
+
+/*
+ * 帐号类,功能:存储帐号信息
+ */
+public class VvsipAccount {
+
+	private String domain;
+	private String userid;
+	private String passwd;
+	private String identity;
+	private String outboundProxy;
+	private String transport;
+	private int registerInterval;
+	private boolean echoCancellation;
+	
+	/**
+	 * @param domain the domain to set
+	 */
+	public void setDomain(String domain) {
+		this.domain = domain;
+	}
+	/**
+	 * @return the domain
+	 */
+	public String getDomain() {
+		return domain;
+	}
+	/**
+	 * @param userid the userid to set
+	 */
+	public void setUserid(String userid) {
+		this.userid = userid;
+	}
+	/**
+	 * @return the userid
+	 */
+	public String getUserid() {
+		return userid;
+	}
+	/**
+	 * @param passwd the passwd to set
+	 */
+	public void setPasswd(String passwd) {
+		this.passwd = passwd;
+	}
+	/**
+	 * @return the passwd
+	 */
+	public String getPasswd() {
+		return passwd;
+	}
+	/**
+	 * @param identity the identity to set
+	 */
+	public void setIdentity(String identity) {
+		this.identity = identity;
+	}
+	/**
+	 * @return the identity
+	 */
+	public String getIdentity() {
+		return identity;
+	}
+	/**
+	 * @param identity the identity to set
+	 */
+	public void setOutboundProxy(String outboundProxy) {
+		this.outboundProxy = outboundProxy;
+	}
+	/**
+	 * @return the identity
+	 */
+	public String getOutboundProxy() {
+		return outboundProxy;
+	}
+	/**
+	 * @param transport the transport to set
+	 */
+	public void setTransport(String transport) {
+		this.transport = transport;
+	}
+	/**
+	 * @return the transport
+	 */
+	public String getTransport() {
+		return transport;
+	}
+	
+	public boolean isDefined() {
+
+		if (domain!=null && domain.length()==0)
+			domain = null;
+		if (userid!=null && userid.length()==0)
+			userid = null;
+		if (passwd!=null && passwd.length()==0)
+			passwd = null;
+		if (identity!=null && identity.length()==0)
+			identity = null;
+		if (outboundProxy!=null && outboundProxy.length()==0)
+			outboundProxy = null;
+		if (transport!=null && transport.length()==0)
+			transport = null;
+		
+		
+		if (transport==null)
+			transport = "udp";
+		else if (transport.compareToIgnoreCase("udp")!=0
+				&&transport.compareToIgnoreCase("tcp")!=0
+				&&transport.compareToIgnoreCase("tls")!=0)
+			transport = "udp";
+		
+		if (registerInterval<=0)
+			registerInterval=600; //default!
+		
+		if (registerInterval<100)
+			registerInterval=100;
+		
+		
+		if (domain==null || domain.length()==0)
+			return false;
+		if (userid==null || userid.length()==0)
+			return false;
+		if (identity==null || identity.length()==0)
+		{
+			identity = "<sip:"+userid+"@"+domain+">";
+		}
+		
+		return true;
+	}
+	
+	/**
+	 * @param registerInterval the registerInterval to set
+	 */
+	public void setRegisterInterval(int registerInterval) {
+		this.registerInterval = registerInterval;
+	}
+	
+	/**
+	 * @return the registerInterval
+	 */
+	public int getRegisterInterval() {
+		return registerInterval;
+	}
+	/**
+	 * @param echoCancellation the echoCancellation to set
+	 */
+	public void setEchoCancellation(boolean echoCancellation) {
+		this.echoCancellation = echoCancellation;
+	}
+	/**
+	 * @return the echoCancellation
+	 */
+	public boolean isEchoCancellation() {
+		return echoCancellation;
+	}
+}

+ 146 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/VvsipCall.java

@@ -0,0 +1,146 @@
+/*
+  vvphone is a SIP app for android.
+  vvsip is a SIP library for softphone (SIP -rfc3261-)
+  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+*/
+
+package com.vvsip.ansip;
+
+import android.graphics.drawable.Drawable;
+import android.os.SystemClock;
+
+import java.io.Serializable;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+/*
+ * 电话呼叫封装类,为主要功能类,功能:提供呼叫相关的功能,包括接电话,挂电话,保持,静音,视频,DTMF
+ */
+public class VvsipCall implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+    public long start_date;
+    public long established_date;
+    public long end_date;
+    public String description;
+    public String mRemoteUri;
+    public int tid;
+    public int cid;
+    public int did;
+    public boolean mIncomingCall;
+    public boolean mMuted;
+    public boolean mOnHold;
+    public int mState;
+    public String mDisplayName;
+    Drawable mContactImage;
+    public boolean mVideoStarted;
+    public String macStr;
+
+    public VvsipCall() {
+        Calendar cal = new GregorianCalendar();
+        start_date = cal.getTime().getTime();
+        description = "--";
+    }
+
+    public boolean isIncomingCall() {
+        return mIncomingCall;
+    }
+
+    public int answer(int code, int enable_audio) {
+        IVvsipService _service = VvsipService.getService();
+        if (_service == null)
+            return -999;
+        VvsipTask _vvsipTask = _service.getVvsipTask();
+        if (_vvsipTask == null)
+            return -998;
+        int i;
+        if (code < 200) {
+            i = _vvsipTask.vvsessionanswer(tid, did, code, 0);
+            if (i >= 0)
+                mState = 1;
+        } else if (code < 300) {
+            i = _vvsipTask.vvsessionanswer(tid, did, code, 1);
+            if (i >= 0) {
+                established_date = SystemClock.elapsedRealtime();
+                mState = 2;
+                AudioOutput.beready = true;
+                AudioInput.beready = true;
+            }
+        } else {
+            i = _vvsipTask.vvsessionanswer(tid, did, code, 0);
+            if (i >= 0) {
+                if (mState < 2 && isIncomingCall()) {
+                    description = ("未接来电");
+                }
+            }
+            if (i >= 0)
+                mState = 3;
+        }
+        return i;
+    }
+
+    public int stop() {
+        IVvsipService _service = VvsipService.getService();
+        if (_service == null)
+            return -999;
+        VvsipTask _vvsipTask = _service.getVvsipTask();
+        if (_vvsipTask == null)
+            return -998;
+        int i = _vvsipTask.vvsessionstop(cid, did, 486);
+        if (i >= 0) {
+            if (mState == 2) {
+                Calendar cal = new GregorianCalendar();
+                end_date = cal.getTime().getTime();
+            } else if (mState < 2 && isIncomingCall()) {
+                description = "未接来电";
+            } else if (mState < 2) {
+                description = "--";
+            }
+        }
+
+        if (i >= 0)
+            mState = 3;
+        return i;
+    }
+
+    public int startvideo() {
+        IVvsipService _service = VvsipService.getService();
+        if (_service == null)
+            return -1;
+        VvsipTask _vvsipTask = _service.getVvsipTask();
+        if (_vvsipTask == null)
+            return -1;
+        int i = _vvsipTask.vvsessionaddvideo(did);
+        if (i >= 0) {
+            mVideoStarted = true;
+            return 0;
+        }
+        return -1;
+    }
+
+    public void sendrtpdtmf(String dtmf) {
+        if (cid > 0 && mState == 2) {
+            IVvsipService _service = VvsipService.getService();
+            if (_service == null)
+                return;
+            VvsipTask _vvsipTask = _service.getVvsipTask();
+            if (_vvsipTask == null)
+                return;
+            //_vvsipTask.vvsessionsenddtmfwithduration(pCall.did, "1", 480);
+            _vvsipTask.vvsessionsendrtpdtmf(did, dtmf);
+        }
+    }
+
+    public void sendinfodtmf(String dtmf) {
+        if (cid > 0 && mState == 2) {
+            IVvsipService _service = VvsipService.getService();
+            if (_service == null)
+                return;
+            VvsipTask _vvsipTask = _service.getVvsipTask();
+            if (_vvsipTask == null)
+                return;
+            _vvsipTask.vvsessionsenddtmfwithduration(did, dtmf, 480);
+        }
+    }
+
+}

+ 58 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/VvsipDTMF.java

@@ -0,0 +1,58 @@
+package com.vvsip.ansip;
+
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+public class VvsipDTMF {
+	
+	private String mTag = "VvsipDTMF";
+	private static VvsipDTMF mVvsipTask;
+	public static int global_failure=0;
+	
+	private static Handler mainActivityMessageHandler;
+	
+	public static VvsipDTMF getVvsipDTMF()  {
+		if (mVvsipTask!=null)
+			return mVvsipTask;
+		return new VvsipDTMF();
+	}
+	
+	public VvsipDTMF() {
+		mVvsipTask = this;
+	}
+	
+	public int testdtmf(int dtmf){
+		Log.e(mTag, "printDTMF:"+dtmf);
+		
+		String strDTMF = null;
+		
+		if (mainActivityMessageHandler != null) {
+			Message m = new Message();
+			m.what = 88;
+			
+			if(dtmf==35){
+				strDTMF = "#";
+			}else if(dtmf==42){
+				strDTMF = "*";
+			}else if(dtmf>=48 && dtmf <=57){
+				strDTMF = String.valueOf(dtmf-48);
+			}else{
+				strDTMF = "unknown";
+			}
+			
+			
+			m.obj = "Received DTMF: " + strDTMF + "\n";
+
+			mainActivityMessageHandler.sendMessage(m);
+		} else {
+			Log.e(mTag, "mainActivityMessageHandler==null");
+		}
+		
+		return 0;
+	}
+	
+	public void setHandler(Handler _mainActivityEventHandler) {
+		mainActivityMessageHandler = _mainActivityEventHandler;
+	}
+}

+ 246 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/VvsipMediaCodecFinder.java

@@ -0,0 +1,246 @@
+/*
+  vvphone is a SIP app for android.
+  vvsip is a SIP library for softphone (SIP -rfc3261-)
+  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+*/
+
+package com.vvsip.ansip;
+
+import android.annotation.TargetApi;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.CodecProfileLevel;
+import android.media.MediaCodecList;
+import android.os.Build;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+
+//Information on encoder, decoder, phone tested...
+
+/*
+HW-INFO:Galaxy Nexus
+HW-INFO:4.3
+HW-INFO:maguro
+HW-INFO:google
+Encoder: OMX.TI.DUCATI1.VIDEO.H264E Mimetype:video/avc supported with NV12 (COLOR_TI_FormatYUV420PackedSemiPlanar)
+Decoder: OMX.TI.DUCATI1.VIDEO.DECODER Mimetype:video/avc supported with NV12 (COLOR_TI_FormatYUV420PackedSemiPlanar)
+
+sidenote: was working with 4.2
+sidenote: MS_NV12 for both decoder/encoder
+sidenote: the decoded data(second plane) is shift by 16 octets?? (end of plane 1 + 16)
+          src_pic.planes[1]=d->data_nv12->b_rptr+d->stride*slice_height+(ysize)/2+16;
+          TODO: verify other received size
+sidenote: decoder tested with CIF and QCIF
+sidenote: encoder tested with 320x240 (and I think CIF and most probably other too...)
+
+HW-INFO:GT-I9305
+HW-INFO:4.1.2
+HW-INFO:m3
+HW-INFO:samsung
+
+Encoder: OMX.SEC.avc.enc Mimetype:video/avc supported with NV21 (COLOR_FormatYUV420SemiPlanar)
+Decoder: OMX.SEC.avc.dec Mimetype:video/avc supported with NV12 (COLOR_FormatYUV420SemiPlanar)
+
+sidenote: ENCODER ********MS_NV21*******
+          DECODER ********MS_NV12*******
+          -> WHY ARE THEY DIFFERENT???
+sidenote: example decoder:
+INFO_OUTPUT_FORMAT_CHANGED: codec name: OMX.SEC.avc.dec
+INFO_OUTPUT_FORMAT_CHANGED: width: 352
+INFO_OUTPUT_FORMAT_CHANGED: height: 288
+INFO_OUTPUT_FORMAT_CHANGED: stride: 352
+INFO_OUTPUT_FORMAT_CHANGED: slice-height: 288
+INFO_OUTPUT_FORMAT_CHANGED: color-format: 21
+INFO_OUTPUT_FORMAT_CHANGED: crop-left: 0
+INFO_OUTPUT_FORMAT_CHANGED: crop-top: 0
+INFO_OUTPUT_FORMAT_CHANGED: crop-right: 351
+INFO_OUTPUT_FORMAT_CHANGED: crop-bottom: 287
+sidenote: decoder tested with CIF and QCIF
+
+
+
+nexus7:
+
+09-04 14:35:32.218: D/VvsipMediaCodecFinder(4104): Encoder: OMX.Nvidia.h264.encoder Mimetype:video/avc supported with YUV420P (COLOR_FormatYUV420Planar)
+09-04 14:35:32.248: D/VvsipMediaCodecFinder(4104): Decoder: OMX.Nvidia.h264.decode Mimetype:video/avc supported with YUV420P (COLOR_FormatYUV420Planar)
+09-04 14:36:15.688: I/VvsipService(4104): HW-INFO:Nexus 7
+09-04 14:36:15.688: I/VvsipService(4104): HW-INFO:4.2.2
+09-04 14:36:15.688: I/VvsipService(4104): HW-INFO:tilapia
+09-04 14:36:15.688: I/VvsipService(4104): HW-INFO:google
+
+09-04 14:40:39.238: E/H264MediaCodecDecoder(4104): INFO_OUTPUT_FORMAT_CHANGED: codec name: OMX.Nvidia.h264.decode
+09-04 14:40:39.988: E/H264MediaCodecDecoder(4104): INFO_OUTPUT_FORMAT_CHANGED: width: 352
+09-04 14:40:39.988: E/H264MediaCodecDecoder(4104): INFO_OUTPUT_FORMAT_CHANGED: height: 288
+09-04 14:40:39.988: E/H264MediaCodecDecoder(4104): INFO_OUTPUT_FORMAT_CHANGED: stride: 352
+09-04 14:40:39.988: E/H264MediaCodecDecoder(4104): INFO_OUTPUT_FORMAT_CHANGED: slice-height: 0
+09-04 14:40:39.988: E/H264MediaCodecDecoder(4104): INFO_OUTPUT_FORMAT_CHANGED: color-format: 19
+09-04 14:40:39.988: E/H264MediaCodecDecoder(4104): INFO_OUTPUT_FORMAT_CHANGED: crop-left: 0
+09-04 14:40:39.988: E/H264MediaCodecDecoder(4104): INFO_OUTPUT_FORMAT_CHANGED: crop-top: 0
+09-04 14:40:39.988: E/H264MediaCodecDecoder(4104): INFO_OUTPUT_FORMAT_CHANGED: crop-right: 351
+09-04 14:40:39.988: E/H264MediaCodecDecoder(4104): INFO_OUTPUT_FORMAT_CHANGED: crop-bottom: 287
+
+sidenote: decoder CIF and QCIF tested
+
+HW-INFO:POV_TAB-PROTAB25XXL8
+HW-INFO:4.1.1
+HW-INFO:POV_TAB-PROTAB25XXL
+HW-INFO:POV
+
+ Decoder: OMX.google.h264.decoder Mimetype:video/avc supported with YUV420P (COLOR_FormatYUV420Planar)
+ No usable Encoder
+ 
+sidenote: receiving QCIF -> crash
+sidenote: second decoder receiving QCIF -> crash (same as OMX.google.h264.decoder?)
+   it reports:  Decoder: video/avc supported with YUV420P (COLOR_FormatYUV420Planar)
+
+
+GT-N7000 and GT-I9100
+
+Encoder: NV12 with COLOR_FormatYUV420SemiPlanar
+
+*/
+/*
+ * 枚举硬编解码器
+ */
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+public class VvsipMediaCodecFinder {
+
+	static final String mTag = "VvsipMediaCodecFinder";
+	
+	static public ArrayList<VvsipMediaCodecInfo> AvcEncoders = new ArrayList<VvsipMediaCodecInfo>();
+	static public ArrayList<VvsipMediaCodecInfo> AvcDecoders = new ArrayList<VvsipMediaCodecInfo>();
+	static public boolean codec_loaded = false;
+    static public int codecCount = 0;
+    static public int totalSize = 0;
+
+    /*
+     * Find whether the given codec is supported
+     */
+    private static boolean isFormatSupported(MediaCodecInfo info, CodecCapabilities cap, int colorFormat) {
+
+        try {
+            for (int k = 0; k < cap.colorFormats.length; ++k) {
+                if (cap.colorFormats[k] == colorFormat) {
+                    //Log.d(mTag, "isFormatSupported: Found " + info.getName() + " // format: " + cap.colorFormats[k]);
+                	return true;
+                }
+            }
+    	}catch (Exception e) {
+            Log.d(mTag, "Codec: Skipping codec " + info.getName());
+            e.printStackTrace();
+		}
+    	return false;
+    }
+    
+	public static int ListAllMediaCodec(String mMimeType, int profile, boolean checkEncoder, boolean checkDecoder) {
+    	
+    	if (Build.VERSION.SDK_INT<16) {
+    		return totalSize;
+    	}
+    	if (AvcEncoders.size()>0)
+    		checkEncoder=false;
+    	if (AvcDecoders.size()>0)
+    		checkDecoder=false;
+    	/* already done or not required */
+    	if (checkEncoder==false && checkDecoder==false) {
+            codec_loaded=true;
+    		return totalSize;
+    	}
+
+		totalSize=0;
+        codecCount = MediaCodecList.getCodecCount();
+        for (int i = 0; i < codecCount; ++i) {
+            MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
+            String[] types = info.getSupportedTypes();
+
+        	if (info.isEncoder()==true && types.length>0)
+        		Log.d(mTag, "Codec: Found " + info.getName() + " // + " + types[0] + " // encoder");
+        	else if (info.isEncoder()==true)
+        		Log.d(mTag, "Codec: Found " + info.getName() + " // decoder");
+        	else if (types.length>0)
+        		Log.d(mTag, "Codec: Found " + info.getName() + " // + " + types[0] + " // decoder");
+        	else
+        		Log.d(mTag, "Codec: Found " + info.getName() + " // decoder");
+
+            totalSize += 1;
+
+            if (checkEncoder==false && info.isEncoder())
+            	continue;
+            if (checkDecoder==false && info.isEncoder()==false)
+            	continue;
+
+        	try {
+                for (int j = 0; j < types.length; ++j) {
+                	int profile_found=0;
+            		if (types[j].compareTo(mMimeType) != 0)
+            			continue;
+                    CodecCapabilities cap = info.getCapabilitiesForType(types[j]);
+                    CodecProfileLevel[] profileLevels = cap.profileLevels;
+                    for (int k = 0; k < profileLevels.length; ++k) {
+                        //Log.d(mTag, "Codec: Found " + info.getName() + " // " + types[j] + " // profile: " + profileLevels[k].profile + " level:" + profileLevels[k].level);
+                        if (profile==profileLevels[k].profile) {
+                        	profile_found=1;
+                        }
+                    }
+                	if (profile_found!=0) {
+                        if (isFormatSupported(info, cap, CodecCapabilities.COLOR_FormatYUV420SemiPlanar)==true) {
+                            Log.d(mTag, "Codec: Found " + info.getName() + " // " + types[j] + " // COLOR_FormatYUV420SemiPlanar");
+                            VvsipMediaCodecInfo apci;
+                    		if (info.getName().equalsIgnoreCase("OMX.Intel.VideoDecoder.AVC"))
+                    			continue; //can't find the correct format...
+                    		else if (info.getName().equalsIgnoreCase("OMX.SEC.avc.enc") && Build.VERSION.SDK_INT<17)
+                    			apci = new VvsipMediaCodecInfo(info, mMimeType, CodecCapabilities.COLOR_FormatYUV420SemiPlanar, 8); // NV21
+                    		else
+                    			apci = new VvsipMediaCodecInfo(info, mMimeType, CodecCapabilities.COLOR_FormatYUV420SemiPlanar, 9); // NV12
+                    		if (info.isEncoder()==true) AvcEncoders.add(apci);
+                    		else AvcDecoders.add(apci);
+                        }
+                        if (isFormatSupported(info, cap, CodecCapabilities.COLOR_FormatYUV420Planar)==true) {
+                            Log.d(mTag, "Codec: Found " + info.getName() + " // " + types[j] + " // COLOR_FormatYUV420Planar");
+                    		VvsipMediaCodecInfo apci = new VvsipMediaCodecInfo(info, mMimeType, CodecCapabilities.COLOR_FormatYUV420Planar, 0); // YUV420P
+                    		if (info.isEncoder()==true) AvcEncoders.add(apci);
+                    		else AvcDecoders.add(apci);
+                        }
+                        if (isFormatSupported(info, cap, CodecCapabilities.COLOR_FormatYUV420PackedPlanar)==true) {
+                            Log.d(mTag, "Codec: Found " + info.getName() + " // " + types[j] + " // COLOR_FormatYUV420PackedPlanar");
+                    		VvsipMediaCodecInfo apci = new VvsipMediaCodecInfo(info, mMimeType, CodecCapabilities.COLOR_FormatYUV420PackedPlanar, 0); // YUV420P
+                    		if (info.isEncoder()==true) AvcEncoders.add(apci);
+                    		else AvcDecoders.add(apci);
+                        }
+                        if (isFormatSupported(info, cap, CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar)==true) {
+                            Log.d(mTag, "Codec: Found " + info.getName() + " // " + types[j] + " // COLOR_FormatYUV420PackedSemiPlanar");
+                    		VvsipMediaCodecInfo apci = new VvsipMediaCodecInfo(info, mMimeType, CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar, 9); // NV12
+                    		if (info.isEncoder()==true) AvcEncoders.add(apci);
+                    		else AvcDecoders.add(apci);
+                        }
+                        if (isFormatSupported(info, cap, CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar)==true) {
+                            Log.d(mTag, "Codec: Found " + info.getName() + " // " + types[j] + " // COLOR_TI_FormatYUV420PackedSemiPlanar");
+                    		VvsipMediaCodecInfo apci = new VvsipMediaCodecInfo(info, mMimeType, CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar, 9); // NV12
+                    		if (info.isEncoder()==true) AvcEncoders.add(apci);
+                    		else AvcDecoders.add(apci);
+                        }
+                        if (isFormatSupported(info, cap, 0x7f000001)==true) {
+                            Log.d(mTag, "Codec: Found " + info.getName() + " // " + types[j] + " // COLOR_TI_FormatYUV420PackedSemiPlanarInterlaced");
+                    		VvsipMediaCodecInfo apci = new VvsipMediaCodecInfo(info, mMimeType, 0x7f000001, 9); // NV12
+                    		if (info.isEncoder()==true) AvcEncoders.add(apci);
+                    		else AvcDecoders.add(apci);
+                        }
+                        if (isFormatSupported(info, cap, CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar)==true) {
+                            Log.d(mTag, "Codec: Found " + info.getName() + " // " + types[j] + " // COLOR_QCOM_FormatYUV420SemiPlanar");
+                    		VvsipMediaCodecInfo apci = new VvsipMediaCodecInfo(info, mMimeType, CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar, 9); // NV12
+                    		if (info.isEncoder()==true) AvcEncoders.add(apci);
+                    		else AvcDecoders.add(apci);
+                        }
+                    }
+                }
+        	}catch (Exception e) {
+                Log.d(mTag, "Codec: Skipping codec " + info.getName());
+                e.printStackTrace();
+			}
+        }
+        codec_loaded=true;
+        return totalSize;
+	}
+}

+ 30 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/VvsipMediaCodecInfo.java

@@ -0,0 +1,30 @@
+/*
+  vvphone is a SIP app for android.
+  vvsip is a SIP library for softphone (SIP -rfc3261-)
+  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+*/
+
+package com.vvsip.ansip;
+
+import android.annotation.TargetApi;
+import android.media.MediaCodecInfo;
+import android.os.Build;
+
+/*
+ * 硬编解码器信息类
+ */
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+public class VvsipMediaCodecInfo  {
+
+	public MediaCodecInfo info;
+	public int mColorFormat;
+	int mMSColorFormat;
+	String mMimeType;
+	
+	VvsipMediaCodecInfo(MediaCodecInfo _info, String _mMimeType, int _mColorFormat, int _mMSColorFormat) {
+		info = _info;
+		mMimeType = _mMimeType;
+		mColorFormat = _mColorFormat;
+		mMSColorFormat = _mMSColorFormat;
+	}
+}

+ 6 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/VvsipNeighbor.java

@@ -0,0 +1,6 @@
+package com.vvsip.ansip;
+
+public class VvsipNeighbor {
+	public String location;
+	public String extension;
+}

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 2637 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/VvsipService.java


+ 23 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/VvsipServiceBinder.java

@@ -0,0 +1,23 @@
+/*
+  vvphone is a SIP app for android.
+  vvsip is a SIP library for softphone (SIP -rfc3261-)
+  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+*/
+
+package com.vvsip.ansip;
+
+import android.os.Binder;
+
+public class VvsipServiceBinder extends Binder {
+
+	private IVvsipService service = null; 
+	  
+    public VvsipServiceBinder(IVvsipService service) { 
+        super(); 
+        this.service = service; 
+    } 
+ 
+    public IVvsipService getService(){ 
+        return service; 
+    } 
+}

+ 415 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/VvsipTask.java

@@ -0,0 +1,415 @@
+/*
+  vvsip is a SIP app for android.
+  vvsip is a SIP library for softphone (SIP -rfc3261-)
+  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+*/
+package com.vvsip.ansip;
+
+
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+/*
+ * JNI接口类,单例模式
+ */
+public class VvsipTask {
+
+	private static VvsipTask mVvsipTask;
+	public static int global_failure=0;
+	
+	public static VvsipTask getVvsipTask()  {
+		if (mVvsipTask!=null)
+			return mVvsipTask;
+		return new VvsipTask();
+	}
+	
+	public VvsipTask() {
+		mVvsipTask = this;
+	}
+	
+	/* REGISTER related events */
+	public static final int EXOSIP_REGISTRATION_SUCCESS=0;       /**< user is successfully registred.  */
+	public static final int EXOSIP_REGISTRATION_FAILURE=1;       /**< user is not registred.           */
+
+	/* INVITE related events within calls */
+	public static final int EXOSIP_CALL_INVITE=2;            /**< announce a new call                   */
+	public static final int EXOSIP_CALL_REINVITE=3;          /**< announce a new INVITE within call     */
+
+	public static final int EXOSIP_CALL_NOANSWER=4;          /**< announce no answer within the timeout */
+	public static final int EXOSIP_CALL_PROCEEDING=5;        /**< announce processing by a remote app   */
+	public static final int EXOSIP_CALL_RINGING=6;           /**< announce ringback                     */
+	public static final int EXOSIP_CALL_ANSWERED=7;          /**< announce start of call                */
+	public static final int EXOSIP_CALL_REDIRECTED=8;        /**< announce a redirection                */
+	public static final int EXOSIP_CALL_REQUESTFAILURE=9;    /**< announce a request failure            */
+	public static final int EXOSIP_CALL_SERVERFAILURE=10;     /**< announce a server failure             */
+	public static final int EXOSIP_CALL_GLOBALFAILURE=11;     /**< announce a global failure             */
+	public static final int EXOSIP_CALL_ACK=12;               /**< ACK received for 200ok to INVITE      */
+
+	public static final int EXOSIP_CALL_CANCELLED=13;         /**< announce that call has been cancelled */
+
+	/* request related events within calls (except INVITE) */
+	public static final int EXOSIP_CALL_MESSAGE_NEW=14;              /**< announce new incoming request. */
+	public static final int EXOSIP_CALL_MESSAGE_PROCEEDING=15;       /**< announce a 1xx for request. */
+	public static final int EXOSIP_CALL_MESSAGE_ANSWERED=16;         /**< announce a 200ok  */
+	public static final int EXOSIP_CALL_MESSAGE_REDIRECTED=17;       /**< announce a failure. */
+	public static final int EXOSIP_CALL_MESSAGE_REQUESTFAILURE=18;   /**< announce a failure. */
+	public static final int EXOSIP_CALL_MESSAGE_SERVERFAILURE=19;    /**< announce a failure. */
+	public static final int EXOSIP_CALL_MESSAGE_GLOBALFAILURE=20;    /**< announce a failure. */
+
+	public static final int EXOSIP_CALL_CLOSED=21;            /**< a BYE was received for this call      */
+
+	/* for both UAS & UAC events */
+	public static final int EXOSIP_CALL_RELEASED=22;             /**< call context is cleared.            */
+
+	/* response received for request outside calls */
+	public static final int EXOSIP_MESSAGE_NEW=23;              /**< announce new incoming request. */
+	public static final int EXOSIP_MESSAGE_PROCEEDING=24;       /**< announce a 1xx for request. */
+	public static final int EXOSIP_MESSAGE_ANSWERED=25;         /**< announce a 200ok  */
+	public static final int EXOSIP_MESSAGE_REDIRECTED=26;       /**< announce a failure. */
+	public static final int EXOSIP_MESSAGE_REQUESTFAILURE=27;   /**< announce a failure. */
+	public static final int EXOSIP_MESSAGE_SERVERFAILURE=28;    /**< announce a failure. */
+	public static final int EXOSIP_MESSAGE_GLOBALFAILURE=29;    /**< announce a failure. */
+
+    /* Presence and Instant Messaging */
+	public static final int EXOSIP_SUBSCRIPTION_NOANSWER=30;          /**< announce no answer              */
+	public static final int EXOSIP_SUBSCRIPTION_PROCEEDING=31;        /**< announce a 1xx                  */
+	public static final int EXOSIP_SUBSCRIPTION_ANSWERED=32;          /**< announce a 200ok                */
+	public static final int EXOSIP_SUBSCRIPTION_REDIRECTED=33;        /**< announce a redirection          */
+	public static final int EXOSIP_SUBSCRIPTION_REQUESTFAILURE=34;    /**< announce a request failure      */
+	public static final int EXOSIP_SUBSCRIPTION_SERVERFAILURE=35;     /**< announce a server failure       */
+	public static final int EXOSIP_SUBSCRIPTION_GLOBALFAILURE=36;     /**< announce a global failure       */
+	public static final int EXOSIP_SUBSCRIPTION_NOTIFY=37;            /**< announce new NOTIFY request     */
+
+	public static final int EXOSIP_IN_SUBSCRIPTION_NEW=38;            /**< announce new incoming SUBSCRIBE.*/
+
+	public static final int EXOSIP_NOTIFICATION_NOANSWER=39;          /**< announce no answer              */
+	public static final int EXOSIP_NOTIFICATION_PROCEEDING=40;        /**< announce a 1xx                  */
+	public static final int EXOSIP_NOTIFICATION_ANSWERED=41;          /**< announce a 200ok                */
+	public static final int EXOSIP_NOTIFICATION_REDIRECTED=42;        /**< announce a redirection          */
+	public static final int EXOSIP_NOTIFICATION_REQUESTFAILURE=43;    /**< announce a request failure      */
+	public static final int EXOSIP_NOTIFICATION_SERVERFAILURE=44;     /**< announce a server failure       */
+	public static final int EXOSIP_NOTIFICATION_GLOBALFAILURE=45;     /**< announce a global failure       */
+    
+	public boolean thread_started=false;
+	
+	private static Handler mainActivityEventHandler;
+	public boolean running=false;
+	private Thread taskThread;
+
+	public native int vvinit(int debug_level);
+	public native int vvreset(int debug_level);
+	public native int vvquit();
+	public native int vvcodecinfomodify(int pos, int enable, String codec_name, int mode, int cng, int vbr);
+	public native int vvvideocodecinfomodify(int pos, int enable, String codec_name, int mode, int cng, int vbr);
+	public native int vvvideocodecattrmodify(int uploadrate, int downloadrate);
+	 /* In 3G mode, if audio bitrate is <=30, the SDP will contain only the lower bitrate codec in SDP */
+	public native int vvcodecattrmodify(int ptime, int audio_bitrate);
+	
+	public native String vvoptiongetversion();
+	public native int vvoptionsetuseragent(String useragent);
+	public native int vvoptionsetinitialaudioport(int initialport);
+	public native int vvoptionenablestunserver(String stunserver, int usestunserver);
+	public native int vvoptionenableturnserver(String  turnserver, int useturnserver);
+	public native int vvoptionsetipv4forgateway(String ipv4forgateway);
+	public native int vvoptionenablerport(int enable);
+	public native int vvoptionsetdnscapabilities(int dnscapabilities);
+	public native int vvoptionenablekeepalive(int interval);
+	public native int vvoptionsetaudioprofile(String profile);
+	public native int vvoptionsetvideoprofile(String profile);
+	public native int vvoptionsettextprofile(String profile);
+	public native int vvoptionsetudpftpprofile(String profile);
+	public native int vvoptionenablesessiontimers(int sessionexpires);
+	public native int vvoptionenablesymmetricrtp(int enable);
+	//public native int vvoptionfindoutsoundcard(struct vvsndcard *sndcard);
+	//public native int vvoptionfindinsoundcard(struct vvsndcard *sndcard);
+	public native int vvoptionselectinsoundcard(int card);
+	public native int vvoptionselectoutsoundcard(int card);
+	//public native int vvoptionselectincustomsoundcard(MSSndCard *captcard);
+	//public native int vvoptionselectoutcustomsoundcard(MSSndCard *playcard);
+	public native int vvoptionsetvolumeoutsoundcard(int card, int mixer, int percent);
+	public native int vvoptiongetvolumeoutsoundcard(int card, int mixer);
+	public native int vvoptionsetvolumeinsoundcard(int card, int percent);
+	public native int vvoptiongetvolumeinsoundcard(int card);
+	public native int vvoptionsetmuteoutsoundcard(int card, int mixer, int val);
+	public native int vvoptionsetmuteinsoundcard(int card, int val);
+	public native int vvoptionenablewebrtcapm(int enable, int aecm, int aecm_comfort_noise, int aecm_routing_mode,
+                                        int ns, int ns_level,
+                                        int agc, int agc_mode, int agc_target_level_dbfs, int agc_compression_gain_db,
+                                        int high_pass_filter);
+	public native int vvoptionenableechocanceller(int enable, int framesize, int taillength);
+	public native int vvoptionenablehalfduplex(int enable, int vadprobstart, int vadprobcontinue);
+	public native int vvoptionenableagc(int enable, int agclevel, int maxgain);
+	public native int vvoptionsetjittermode(int mode);
+	public native int vvoptionvideosetjittermode(int mode);
+	public native int vvoptionsetpassword(String realm, String login, String passwd);
+	public native int vvoptionloadplugins(String directory);
+	public native int vvoptionenableoptionnalencryption(int optionnalencryption);
+	public native int vvoptionsetdscpvalue(int dscpvalue);
+	public native int vvoptionsetaudiodscp(int dscpvalue);
+	public native int vvoptionsetvideodscp(int dscpvalue);
+	public native int vvoptionsettextdscp(int dscpvalue);
+	public native int vvoptionsetudpftpdscp(int dscpvalue);
+	public native int vvoptionadddnscache(String host, String ip);
+	public native int vvoptionsetsupportedextensions(String supportedextensions);
+	public native int vvoptionsetacceptedtypes(String acceptedtypes);
+	public native int vvoptionsetallowedmethods(String allowedmethods);
+	public native int vvoptionsetrate(int rate);
+	public native int vvoptionsetvolumegain(float capturegain, float playbackgain);
+	public native int vvoptionsetecholimitation(int enabled,
+			float threshold,
+			float speed,
+			float force,
+			int sustain);
+
+	public native long vveventget();
+	public native long vveventwait(int tvs, int tvms);
+	public native void vveventrelease(long evt);
+	public native int vveventgettype(long evt);
+	public native int vveventgettid(long evt);
+	public native int vveventgetcid(long evt);
+	public native int vveventgetdid(long evt);
+	public native int vveventgetrid(long evt);
+	public native int vveventgetnid(long evt);
+	public native int vveventgetsid(long evt);
+	public native String vveventgetmethod(long evt);
+	public native String vveventgetreason(long evt);
+	public native int vveventgetstatuscode(long evt);
+	public native String vveventgetrequestheader(long evt, String hname, int n);
+	public native String vveventgetresponseheader(long evt, String hname, int n);
+	public native String vvurigetusername(String mSipUri);
+	public native String vvurigetdomain(String mSipUri);
+	public native String vvurigetdisplayname(String mSipUri);
+	
+	//For retreiving body in REQUEST (MESSAGE, INVITE, etc..)
+	public native byte[] vveventgetrequestbody(long evt, int n);
+
+	//public native int vvnetworkguessip(int family, String address, int size);
+	public native String vvnetworkgetnatinfo(String ip, int port);
+	public native int vvnetworkmasquerade(String ip, int port);
+	public native int vvoptionsettlscertificate(String certificate, String priv_key, String root_ca); 
+	public native int vvnetworkstart(String transport, int port);
+
+	public native int vvregisterstart(String identity, String proxy, String outboundproxy, int expires);
+	public native int vvregisterrefresh(int rid, int expires);
+	public native int vvregisterstop(int rid);
+
+	public native int vvsessionstart(String identity, String target, String proxy, String outboundproxy,String tags);
+	public native int vvsessionstartwithvideo(String identity, String target, String proxy, String outboundproxy,String tags);
+	
+/*	public native int vvsessionstart(String identity, String target, String proxy, String outboundproxy);
+	public native int vvsessionstartwithvideo(String identity, String target, String proxy, String outboundproxy);*/
+	public native int vvsessionaddvideo(int did);
+	public native int vvsessionstopvideo(int did);
+    public native int vvsessionadaptvideobitrate(int did, int percent);
+	public native int vvmessagegetrequestaudiortpdirection(long evt);
+	public native int vvmessagegetrequestvideortpdirection(long evt);
+	public native int vvmessagegetresponsevideortpdirection(long evt);
+	public native int vvsessionstop(int cid, int did, int code);
+	public native int vvsessionanswer(int cid, int did, int code, int enable_audio);
+
+	
+	public native float vvsessiongetaudiouploadbandwidth(int did);
+	public native float vvsessiongetaudiodownloadbandwidth(int did);
+	public native float vvsessiongetaudiopacketloss(int did);
+	public native float vvsessiongetaudioremotepacketloss(int did);
+	
+
+	public native float vvsessiongetvideouploadbandwidth(int did);
+	public native float vvsessiongetvideodownloadbandwidth(int did);
+	public native float vvsessiongetvideopacketloss(int did);    
+	public native float vvsessiongetvideoremotepacketloss(int did);
+	
+	public native int vvsessionanswerrequest(int cid, int did, int code);
+
+	public native int vvsessionsenddtmfwithduration(int did, String dtmf, int duration);
+	public native int vvsessionsendinbanddtmf(int did, String dtmf);
+	public native int vvsessionsendrtpdtmf(int did, String dtmf);
+	
+	public native int vvexecuteuri(String identity, String target,
+			String proxy, String outboundproxy, String body);
+	
+	public native int vvmessageanswer(int tid, int code);
+	
+	public native void registeraudio();
+	
+	//EXPERIMENTAL USAGE OF MEDIACODEC API FOR SDK>=16
+	//sidenote: validation of every device is NECESSARY.
+	//development device is galaxy nexus
+	public native int registermsh264mediaencoder(int version_sdk);
+	public native int registermsh264mediadecoder(int version_sdk);
+	
+	public native int vvoptionenablefilter(String filter_name);
+	public native int vvoptiondisablefilter(String filter_name);
+	
+	public native int setvideodisplay(Object lVideoDisplay);
+
+	/* load the 'vvsip' library on startup.
+	 * The library has been unpacked into
+	 * /data/data/com.vvsip.vvsip/lib/vvsip.so
+	 */
+	static {
+		CheckCpu ccpu;
+		int isArm = -1; 
+		int features = 0;
+		
+		try {
+			ccpu = new CheckCpu();
+			isArm = ccpu.getcpufamily();
+			features = ccpu.getcpufeatures();
+		} catch (UnsatisfiedLinkError e) {
+			Log.e("VvsipTask", "native library is missing // re-install the application...");
+			e.printStackTrace();
+			global_failure=1;
+		} catch (Exception e) {
+			Log.e("VvsipTask", "problem loading checkcpu.so?");
+			e.printStackTrace();
+			global_failure=1;
+		}
+		
+		if (isArm==1)
+		{
+			Log.i("VvsipTask", "CPU feature: " + features);
+			if ((features & 0x001) == 0x001)
+				Log.i("VvsipTask", "CPU feature: ANDROID_CPU_ARM_FEATURE_ARMv7");
+			if ((features & 0x002) == 0x002)
+				Log.i("VvsipTask", "CPU feature: ANDROID_CPU_ARM_FEATURE_VFPv3");
+			if ((features & 0x004) == 0x004)
+				Log.i("VvsipTask", "CPU feature: ANDROID_CPU_ARM_FEATURE_NEON");
+
+			try {
+				if ((features & 0x001) == 0x001)
+				{
+					if ((features & 0x004) == 0x004)
+						System.loadLibrary("vvsip-v7a-neon");
+					else
+						System.loadLibrary("vvsip-v7a");
+				} else {
+					System.loadLibrary("vvsip-v5");
+				}
+			} catch (UnsatisfiedLinkError e) {
+				Log.e("VvsipTask", "native library is missing // re-install the application...");
+				e.printStackTrace();
+				global_failure=1;
+			} catch (Exception e) {
+				Log.e("VvsipTask", "problem loading arm vvsip?");
+				e.printStackTrace();
+				global_failure=1;
+			}
+		} else if (isArm==2)
+			{
+				Log.i("VvsipTask", "CPU feature: " + features);
+				if ((features & 0x001) == 0x001)
+					Log.i("VvsipTask", "CPU feature: ANDROID_CPU_X86_FEATURE_SSSE3");
+				if ((features & 0x002) == 0x002)
+					Log.i("VvsipTask", "CPU feature: ANDROID_CPU_X86_FEATURE_POPCNT");
+				if ((features & 0x004) == 0x004)
+					Log.i("VvsipTask", "CPU feature: ANDROID_CPU_X86_FEATURE_MOVBE");
+				try {
+					System.loadLibrary("vvsip-x86");
+				} catch (UnsatisfiedLinkError e) {
+					Log.i("VvsipTask", "native library is missing // re-install the application...");
+					e.printStackTrace();
+					global_failure=1;
+				} catch (Exception e) {
+					Log.i("VvsipTask", "problem loading x86 vvsip?");
+					e.printStackTrace();
+					global_failure=1;
+				}
+		} else {
+			try {
+				System.loadLibrary("vvsip");
+			} catch (UnsatisfiedLinkError e) {
+				Log.e("VvsipTask", "native library is missing // re-install the application...");
+				e.printStackTrace();
+				global_failure=1;
+			} catch (Exception e) {
+				Log.e("VvsipTask", "problem loading arm vvsip?");
+				e.printStackTrace();
+				global_failure=1;
+			}
+		}
+		
+	}	
+
+	public void stop() {
+		
+		if (thread_started==false)
+			return;
+		running = false;
+
+		if (taskThread!=null)
+		{
+			try {
+				taskThread.join(1000);
+			} catch (InterruptedException e) {
+				e.printStackTrace();
+			}
+			taskThread = null;
+		}
+   		
+		while (thread_started==true)
+		{
+
+		}
+	}
+
+	/*
+	 * 开始Handler,获得SIP信令消息与状态
+	 */
+	public void start(Handler _mainActivityEventHandler) {
+
+	   	if (running==true)
+    		return;
+
+	   	running=true;
+		thread_started = true;
+
+		mainActivityEventHandler = _mainActivityEventHandler;
+
+		taskThread = new Thread() {
+			@Override public void run() {
+				vvsipLoop();
+				thread_started = false;
+			}
+		};
+		taskThread.start();
+	}
+
+	private void vvsipLoop() {
+		Log.i(getClass().getSimpleName(), "background task - start");
+
+		int count=0;
+		while (running==true) {
+
+			count++;
+			if (count%(5*3)==0) /* each 3seconds */
+			{
+				Message m = Message.obtain(); //new Message(); 
+				m.what = -1;
+				m.arg1 = -1;
+				m.arg2 = -1;
+
+				mainActivityEventHandler.sendMessage(m);
+				count=0;
+			}
+			long evt = vveventwait(0, 200);
+			if (evt!=0)
+			{
+				int type = vveventgettype(evt);
+
+				//Send update to the main thread
+				Message m = Message.obtain(); //new Message();
+				m.what = type;
+				m.arg1 = vveventgetcid(evt);
+				m.arg2 = vveventgetdid(evt);
+				Long obj = Long.valueOf(evt);
+				m.obj = obj;
+
+				mainActivityEventHandler.sendMessage(m); 
+			}
+		}
+		Log.i(getClass().getSimpleName(), "background task - end");
+	}
+
+}

+ 591 - 0
AmDemo_R/src/main/java/com/vvsip/ansip/video/VideoCameraPreview.java

@@ -0,0 +1,591 @@
+/*
+  vvphone is a SIP app for android.
+  vvsip is a SIP library for softphone (SIP -rfc3261-)
+  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+ */
+
+package com.vvsip.ansip.video;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.ImageFormat;
+import android.hardware.Camera;
+import android.hardware.Camera.PreviewCallback;
+import android.hardware.Camera.Size;
+import android.os.Build;
+import android.preference.PreferenceManager;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ *  Camera SurfaceView的封装
+ */
+public class VideoCameraPreview extends ViewGroup implements SurfaceHolder.Callback {
+	private final String TAG = "VideoCameraPreview";
+
+	SurfaceView mSurfaceView;
+	SurfaceHolder mHolder;
+	Size mPreviewSize;
+	List<Size> mSupportedPreviewSizes;
+	List<Integer> mSupportedFormats;
+	Camera mCamera;
+
+	private PreviewCallback mPreviewCallback = null;
+	private byte[] camera_preview_buffer = null;
+	DisplayMetrics metrics = new DisplayMetrics();
+
+	private Integer rotate_selfview_display = 0;
+
+	private boolean mHasActiveSurface=false;
+
+	public VideoCameraPreview(Context context, AttributeSet attributes) {
+		super(context, attributes);
+
+		mSurfaceView = new SurfaceView(context);
+		mSurfaceView.setZOrderOnTop(true);
+		addView(mSurfaceView);
+
+		// Install a SurfaceHolder.Callback so we get notified when the
+		// underlying surface is created and destroyed.
+		mHolder = mSurfaceView.getHolder();
+		mHolder.addCallback(this);
+		mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+	}
+
+	public void setPreviewCallback(PreviewCallback previewCallback) {
+		mPreviewCallback = previewCallback;
+	}
+
+	public void setCamera(Camera camera) {
+		mCamera = camera;
+		if (mCamera != null) {
+			Camera.Parameters params;
+			try {
+				params = mCamera.getParameters();
+			} catch (Exception e) {
+				// detected by report: java.lang.RuntimeException: getParameters
+				// failed (empty parameters)
+				Log.e(TAG, "Exception caused by mCamera.getParameters()", e);
+				mCamera = null;
+				return;
+			}
+
+			if (Build.VERSION.SDK_INT <= 4) {
+				String previewSizeValueString = params.get("preview-size-values");
+				Size lSize = params.getPreviewSize();
+				if (previewSizeValueString != null && previewSizeValueString.length() > 0) {
+					if (previewSizeValueString.contains("176x144")) {
+						lSize.width = 176;
+						lSize.height = 144;
+					} else if (previewSizeValueString.contains("320x240")) {
+						lSize.width = 320;
+						lSize.height = 240;
+					} else if (previewSizeValueString.contains("352x288")) {
+						lSize.width = 352;
+						lSize.height = 288;
+					} else if (previewSizeValueString.contains("640x480")) {
+						lSize.width = 640;
+						lSize.height = 480;
+					} else if (previewSizeValueString.contains("480x320")) {
+						lSize.width = 480;
+						lSize.height = 320;
+					}
+					// else, choose the current defined size...
+				}
+
+				int lFormat = params.getPreviewFormat();
+
+				mSupportedFormats = new ArrayList<Integer>();
+				mSupportedFormats.add(lFormat);
+
+				Log.i("VvsipVideoPreview", "using " + lSize.width + "x" + lSize.height + " for camera size");
+
+				mSupportedPreviewSizes = new ArrayList<Size>();
+				mSupportedPreviewSizes.add(lSize);
+			} else {
+				mSupportedPreviewSizes = params.getSupportedPreviewSizes();
+				Log.i("VvsipVideoPreview", "params.getSupportedPreviewSizes " + mSupportedPreviewSizes);
+
+				try {
+					mSupportedFormats = params.getSupportedPreviewFormats();
+				} catch (Exception e) {
+					mSupportedFormats = null;
+				}
+
+				if (mSupportedFormats == null) {
+					int lFormat = params.getPreviewFormat();
+					mSupportedFormats = new ArrayList<Integer>();
+					mSupportedFormats.add(lFormat);
+				}
+			}
+			requestLayout();
+		}
+	}
+
+	public void switchCamera(Camera camera) {
+		setCamera(camera);
+		try {
+			camera.setPreviewDisplay(mHolder);
+			if (Build.VERSION.SDK_INT < 8) {
+				camera.setPreviewCallback(mPreviewCallback);
+			} else {
+				camera.setPreviewCallbackWithBuffer(mPreviewCallback);
+			}
+		} catch (IOException exception) {
+			Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
+		}
+
+		if (mSupportedFormats != null) {
+			getOptimalSupportedFormat(mSupportedFormats);
+		}
+
+		if (mSupportedPreviewSizes != null) {
+			SharedPreferences mConfiguration;
+			mConfiguration = PreferenceManager.getDefaultSharedPreferences(this.getContext());
+			String val = mConfiguration.getString("key_video_size", "default");
+			if (val == null || val.compareToIgnoreCase("0") == 0 || val.compareToIgnoreCase("1") == 0
+					|| val.compareToIgnoreCase("default") == 0) {
+				mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, 320, 240);
+			} else if (val.compareToIgnoreCase("small") == 0) {
+				mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, 240, 160);
+			} else if (val.compareToIgnoreCase("normal") == 0) {
+				mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, 320, 240);
+			} else if (val.compareToIgnoreCase("large") == 0) {
+				mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, 640, 480);
+			} else if (val.compareToIgnoreCase("hd") == 0) {
+				mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, 1280, 720);
+			} else {
+				mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, 320, 240);
+			}
+
+			// mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes,
+			// width, heigth);
+			// mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, 240,
+			// 160);
+			// mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, 352,
+			// 288);
+		}
+
+		Camera.Parameters parameters = camera.getParameters();
+		parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
+		requestLayout();
+
+		if (Build.VERSION.SDK_INT <= 4) {
+			parameters.set("preview-frame-rate", 1);
+			parameters.setPreviewFrameRate(1);
+		}
+
+		/* SIBO-Q899 failure? */
+		try  {
+			camera.setParameters(parameters);
+		} catch (Exception e) {
+			Log.e(TAG, "Exception for camera.setParameters(parameters)", e);
+			camera_preview_buffer = null;
+		}
+
+		try {
+			if (Build.VERSION.SDK_INT >= 8) {
+				if (camera_preview_buffer == null)
+					camera_preview_buffer = new byte[1800000];
+				camera.addCallbackBuffer(camera_preview_buffer);
+			}
+		} catch (Exception e) {
+			Log.e(TAG, "Exception for allocation of camera_preview_buffer", e);
+			camera_preview_buffer = null;
+		}
+
+	}
+
+	@Override
+	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+		// We purposely disregard child measurements because act as a
+		// wrapper to a SurfaceView that centers the camera preview instead
+		// of stretching it.
+		// final int width = resolveSize(getSuggestedMinimumWidth(),
+		// widthMeasureSpec);
+		// final int height = resolveSize(getSuggestedMinimumHeight(),
+		// heightMeasureSpec);
+		// setMeasuredDimension(width, height);
+
+		WindowManager wm = (WindowManager) this.getContext().getSystemService(Context.WINDOW_SERVICE);
+		wm.getDefaultDisplay().getMetrics(metrics);
+
+		float screen_Xsize = metrics.widthPixels / metrics.xdpi;
+		float screen_Ysize = metrics.heightPixels / metrics.ydpi;
+		Log.i("VideoCameraPreview", "screen size = " + screen_Xsize + "x" + screen_Ysize + "inch " + metrics.widthPixels + "x"
+				+ metrics.heightPixels + "px");
+
+		// example on nexus S:
+		// 3.4120736x2.031496 //landscape nexus S
+		// 2.047244x3.3858268 //portait nexus S
+		// if (screen_Xsize>screen_Ysize) {
+		// if (screen_Xsize<4.5)
+		// setMeasuredDimension(160, 120);
+		// else if (metrics.widthPixels/320>2)
+		// setMeasuredDimension(320, 240);
+		// else
+		// setMeasuredDimension(160, 120);
+		// } else {
+		// if (screen_Ysize<4.5)
+		// setMeasuredDimension(160, 120);
+		// else if (metrics.heightPixels/240>2)
+		// setMeasuredDimension(320, 240);
+		// else
+		// setMeasuredDimension(160, 120);
+		// }
+
+		if (mSupportedFormats != null) {
+			getOptimalSupportedFormat(mSupportedFormats);
+		}
+
+		if (mSupportedPreviewSizes != null) {
+			SharedPreferences mConfiguration;
+			mConfiguration = PreferenceManager.getDefaultSharedPreferences(this.getContext());
+			String val = mConfiguration.getString("key_video_size", "default");
+			if (val == null || val.compareToIgnoreCase("0") == 0 || val.compareToIgnoreCase("1") == 0
+					|| val.compareToIgnoreCase("default") == 0) {
+				Log.w("VvsipService", "old config found");
+				mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, 320, 240);
+			} else if (val.compareToIgnoreCase("small") == 0) {
+				mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, 240, 160);
+			} else if (val.compareToIgnoreCase("normal") == 0) {
+				mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, 320, 240);
+			} else if (val.compareToIgnoreCase("large") == 0) {
+				mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, 640, 480);
+			} else if (val.compareToIgnoreCase("hd") == 0) {
+				mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, 1280, 720);
+			}
+			// mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes,
+			// width, heigth);
+			// mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, 240,
+			// 160);
+			// mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, 352,
+			// 288);
+		}
+		if (mPreviewSize != null) {
+			// Log.i("VideoCameraPreview",
+			// "onMeasure done // rotate_selfview_display = " +
+			// rotate_selfview_display + " optimal size = " + mPreviewSize.width
+			// + "x" + mPreviewSize.height);
+
+			int mPreviewSize_width;
+			int mPreviewSize_height;
+			if (rotate_selfview_display == 90 || rotate_selfview_display == 270) {
+				mPreviewSize_width = mPreviewSize.height;
+				mPreviewSize_height = mPreviewSize.width;
+			} else {
+				mPreviewSize_width = mPreviewSize.width;
+				mPreviewSize_height = mPreviewSize.height;
+			}
+			float imageSideRatio = (float) mPreviewSize_width / (float) mPreviewSize_height;
+			float viewSideRatio = (float) MeasureSpec.getSize(widthMeasureSpec) / (float) MeasureSpec.getSize(heightMeasureSpec);
+
+			if (imageSideRatio >= viewSideRatio) {
+				// Image is wider than the display (ratio)
+				int width = MeasureSpec.getSize(widthMeasureSpec);
+				int height = (int) (width / imageSideRatio);
+				setMeasuredDimension(width, height);
+			} else {
+				// Image is taller than the display (ratio)
+				int height = MeasureSpec.getSize(heightMeasureSpec);
+				int width = (int) (height * imageSideRatio);
+				setMeasuredDimension(width, height);
+			}
+		} else {
+			setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
+		}
+	}
+
+	@Override
+	protected void onLayout(boolean changed, int l, int t, int r, int b) {
+		if (changed && getChildCount() > 0) {
+			final View child = getChildAt(0);
+
+			final int width = r - l;
+			final int height = b - t;
+
+			int previewWidth = width;
+			int previewHeight = height;
+			if (mPreviewSize != null) {
+				if (rotate_selfview_display == 90 || rotate_selfview_display == 270) {
+					previewWidth = mPreviewSize.height;
+					previewHeight = mPreviewSize.width;
+				} else {
+					previewWidth = mPreviewSize.width;
+					previewHeight = mPreviewSize.height;
+				}
+			}
+
+			// Center the child SurfaceView within the parent.
+			if (width * previewHeight > height * previewWidth) {
+				final int scaledChildWidth = previewWidth * height / previewHeight;
+				child.layout((width - scaledChildWidth) / 2, 0, (width + scaledChildWidth) / 2, height);
+			} else {
+				final int scaledChildHeight = previewHeight * width / previewWidth;
+				child.layout(0, (height - scaledChildHeight) / 2, width, (height + scaledChildHeight) / 2);
+			}
+		}
+	}
+
+	public void surfaceCreated(SurfaceHolder holder) {
+		// The Surface has been created, acquire the camera and tell it where
+		// to draw.
+		try {
+			if (mCamera != null) {
+				mCamera.setPreviewDisplay(holder);
+				if (Build.VERSION.SDK_INT < 8) {
+					mCamera.setPreviewCallback(mPreviewCallback);
+				} else {
+					mCamera.setPreviewCallbackWithBuffer(mPreviewCallback);
+				}
+
+				try {
+					if (Build.VERSION.SDK_INT >= 8) {
+						if (camera_preview_buffer == null)
+							camera_preview_buffer = new byte[1800000];
+						mCamera.addCallbackBuffer(camera_preview_buffer);
+					}
+					Log.i(TAG, "camera_preview_buffer allocated");
+				} catch (Exception e) {
+					Log.e(TAG, "Exception for allocation of camera_preview_buffer", e);
+					camera_preview_buffer = null;
+				}
+			}
+		} catch (IOException exception) {
+			Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
+		}
+	}
+
+	public void surfaceDestroyed(SurfaceHolder holder) {
+		// Surface will be destroyed when we return, so stop the preview.
+		mHasActiveSurface=false;
+		if (mCamera != null) {
+			if (Build.VERSION.SDK_INT < 8) {
+				mCamera.setPreviewCallback(null);
+			} else {
+				mCamera.setPreviewCallbackWithBuffer(null);
+			}
+			mCamera.stopPreview();
+		}
+	}
+
+	public int getOptimalSupportedFormat(List<Integer> formats) {
+		int format = -1;
+
+		for (Integer fmt : formats) {
+			if (fmt.intValue() == ImageFormat.NV21) {
+				Log.i(TAG, "format: ImageFormat.NV21");
+			} else if (fmt.intValue() == ImageFormat.YUY2) {
+				Log.i(TAG, "format: ImageFormat.YUY2");
+			} else if (fmt.intValue() == ImageFormat.NV16) { // YUV422P
+				Log.i(TAG, "format: ImageFormat.NV16");
+			} else {
+				Log.i(TAG, "format: -not supported-" + fmt);
+			}
+		}
+
+		for (Integer fmt : formats) {
+			if (fmt.intValue() == ImageFormat.NV21) {
+				format = ImageFormat.NV21;
+				return format;
+			}
+		}
+
+		for (Integer fmt : formats) {
+			if (fmt.intValue() == ImageFormat.YUY2) {
+				format = ImageFormat.YUY2;
+				return format;
+			}
+		}
+
+		return format;
+	}
+
+	private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
+		final double ASPECT_TOLERANCE = 0.1;
+		double targetRatio = (double) w / h;
+		if (sizes == null)
+			return null;
+
+		Size optimalSize = null;
+		double minDiff = Double.MAX_VALUE;
+
+		int targetHeight = h;
+
+		// Try to find an size match aspect ratio and size
+		for (Size size : sizes) {
+			Log.i("VvsipVideoPreview", "size list " + size.width + "x" + size.height);
+			double ratio = (double) size.width / size.height;
+			if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
+				continue;
+			if (Math.abs(size.height - targetHeight) < minDiff) {
+				optimalSize = size;
+				minDiff = Math.abs(size.height - targetHeight);
+			}
+		}
+
+		// Cannot find the one match the aspect ratio, ignore the requirement
+		if (optimalSize == null) {
+			minDiff = Double.MAX_VALUE;
+			for (Size size : sizes) {
+				if (Math.abs(size.height - targetHeight) < minDiff) {
+					optimalSize = size;
+					minDiff = Math.abs(size.height - targetHeight);
+				}
+			}
+		}
+
+		if (optimalSize == null) {
+			for (Size size : sizes) {
+				optimalSize = size;
+			}
+		}
+
+		if (Build.DEVICE.toUpperCase(Locale.US).startsWith("GT-P1010") == true) {
+			optimalSize.height = 480;
+			optimalSize.width = 640;
+		}
+
+		Log.i("VvsipVideoPreview", "final choice " + optimalSize.width + "x" + optimalSize.height);
+		return optimalSize;
+	}
+
+	static public int find_range_above(List<int[]> fpslist, int higher_than) {
+		int max = 100000;
+		int best_index = -1;
+		for (int i = 0; i < fpslist.size(); i++) {
+			int tmp_maxfps = fpslist.get(i)[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
+			if (tmp_maxfps >= higher_than && max > tmp_maxfps) {
+				max = tmp_maxfps;
+				best_index = i;
+			}
+		}
+		if (max == 0)
+			return -1;
+		return best_index;
+	}
+
+	static public int find_range_below(List<int[]> fpslist, int lower_than) {
+		int max = 0;
+		int best_index = -1;
+		for (int i = 0; i < fpslist.size(); i++) {
+			int tmp_maxfps = fpslist.get(i)[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
+			if (tmp_maxfps <= lower_than && max < tmp_maxfps) {
+				max = tmp_maxfps;
+				best_index = i;
+			}
+		}
+		if (max == 0)
+			return -1;
+		return best_index;
+	}
+
+	@TargetApi(9)
+	public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+		// Now that the size is known, set up the camera parameters and begin
+		// the preview.
+		mHasActiveSurface=true;
+		if (mCamera != null) {
+			Camera.Parameters parameters;
+			try {
+				parameters = mCamera.getParameters();
+			} catch (Exception e) {
+				// detected by report: java.lang.RuntimeException: getParameters
+				// failed (empty parameters)
+				Log.e(TAG, "Exception caused by mCamera.getParameters()", e);
+				mCamera = null;
+				return;
+			}
+
+			// java.lang.NullPointerException
+			// at
+			// com.vvsip.ansip.video.VideoCameraPreview.surfaceChanged(VideoCameraPreview.java:437)
+			// -> means mPreviewSize is null? or parameters?
+
+			try {
+				parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
+				Log.i("VvsipVideoPreview", "parameters.setPreviewSize " + mPreviewSize.width + "x" + mPreviewSize.height
+						+ " for camera size");
+			} catch (Exception e) {
+				Log.d(TAG, "Failed to call setPreviewSize");
+				if (mPreviewSize == null)
+					Log.d(TAG, "mPreviewSize is null");
+			}
+			if (Build.VERSION.SDK_INT <= 4) {
+				parameters.set("preview-frame-rate", 1);
+				parameters.setPreviewFrameRate(1);
+			} else if (Build.VERSION.SDK_INT < 9) {
+				parameters.setPreviewFrameRate(15);
+			} else {
+				int[] fpsrange = new int[2];
+				parameters.getPreviewFpsRange(fpsrange);
+				Log.d(TAG, "cur minfps= " + fpsrange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]);
+				Log.d(TAG, "cur maxfps= " + fpsrange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+
+				try {
+					// crash received on GT-N7000 with
+					// getSupportedPreviewFpsRange returning null
+					List<int[]> fpslist = parameters.getSupportedPreviewFpsRange();
+
+					Log.d(TAG, "size= " + fpslist.size());
+					for (int i = 0; i < fpslist.size(); i++) {
+						Log.d(TAG, i + "found fps= " + fpslist.get(i)[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]);
+						Log.d(TAG, i + "found fps= " + fpslist.get(i)[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+					}
+					int index_1 = find_range_below(fpslist, 15000);
+					int index_2 = find_range_above(fpslist, 15000);
+					int index = -1;
+					if (index_1 >= 0 && index_2 >= 0) {
+						if (fpslist.get(index_1)[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] >= 10000)
+							index = index_1;
+						else
+							index = index_2;
+					} else if (index < 0 && index_1 >= 0) {
+						index = index_1;
+					} else if (index < 0 && index_2 >= 0) {
+						index = index_2;
+					}
+
+					if (index >= 0) {
+						Log.d(TAG, " new fps= " + fpslist.get(index)[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]);
+						Log.d(TAG, " new fps= " + fpslist.get(index)[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+						parameters.setPreviewFpsRange(fpslist.get(index)[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+								fpslist.get(index)[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+					}
+				} catch (Exception e) {
+					Log.d(TAG, "Failed to get/set FPS");
+				}
+			}
+			requestLayout();
+
+			try {
+				mCamera.setParameters(parameters);
+				mCamera.startPreview();
+			} catch (Exception exception) {
+				Log.e(TAG, "Exception caused by startPreview()", exception);
+			}
+		}
+	}
+
+	public void setRotateValue(Integer _rotate_selfview_display) {
+		// TODO Auto-generated method stub
+		rotate_selfview_display = _rotate_selfview_display;
+	}
+
+	public boolean hasActiveSurface() {
+		// TODO Auto-generated method stub
+		return mHasActiveSurface;
+	}
+}

+ 288 - 0
AmDemo_R/src/main/java/com/vvsip/viewsip/VideoDisplay.java

@@ -0,0 +1,288 @@
+/*
+  vvphone is a SIP app for android.
+  vvsip is a SIP library for softphone (SIP -rfc3261-)
+  Copyright (C) 2003-2010  Bluegoby - <bluegoby@163.com>
+ */
+package com.vvsip.viewsip;
+
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+import com.example.amdemo_r.R;
+import com.vvsip.ansip.IVvsipService;
+import com.vvsip.ansip.VvsipService;
+import com.vvsip.ansip.VvsipTask;
+
+/**
+ * SurfaceView的封装,功能:显示对方视频
+ */
+public class VideoDisplay extends SurfaceView {
+    private final String mTag = "VideoDisplay";
+
+    private Bitmap mStaticImage;
+    private SurfaceHolder holder;
+
+    private Bitmap mIncomingImage;
+
+    private boolean running = false;
+
+    int mVideoViewWidth;
+    int mVideoViewHeight;
+
+    private int onscreen_width = 0;
+    private int onscreen_height = 0;
+    private int android_scaling = 0;
+    Rect orig = null;
+    Rect dest = null;
+
+    public VideoDisplay(Context context, AttributeSet attrset) {
+        super(context, attrset);
+        mStaticImage = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
+        holder = getHolder();
+        holder.addCallback(new SurfaceHolder.Callback() {
+
+            @Override
+            public void surfaceDestroyed(SurfaceHolder holder) {
+
+                IVvsipService _service = VvsipService.getService();
+                if (_service == null)
+                    return;
+                VvsipTask _vvsipTask = _service.getVvsipTask();
+                if (_vvsipTask == null)
+                    return;
+                running = false;
+                Log.i(mTag, "videoout: VideoDisplay removed");
+                _vvsipTask.setvideodisplay(null);
+                if (mIncomingImage != null)
+                    mIncomingImage.recycle();
+                mIncomingImage = null;
+            }
+
+            @SuppressLint("WrongCall")
+            @Override
+            public void surfaceCreated(SurfaceHolder holder) {
+
+                try {
+                    Canvas c = holder.lockCanvas(null);
+                    onDraw(c);
+                    holder.unlockCanvasAndPost(c);
+                } catch (Exception e) {
+
+                }
+
+                IVvsipService _service = VvsipService.getService();
+                if (_service == null)
+                    return;
+                VvsipTask _vvsipTask = _service.getVvsipTask();
+                if (_vvsipTask == null)
+                    return;
+                _vvsipTask.setvideodisplay(VideoDisplay.this);
+                running = true;
+                Log.i(mTag, "videoout: VideoDisplay provided");
+            }
+
+            @Override
+            public void surfaceChanged(SurfaceHolder holder, int format,
+                                       int width, int height) {
+            }
+        });
+    }
+
+    public VideoDisplay(Context context) {
+        super(context);
+        mStaticImage = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
+        holder = getHolder();
+        holder.addCallback(new SurfaceHolder.Callback() {
+
+            @Override
+            public void surfaceDestroyed(SurfaceHolder holder) {
+
+                IVvsipService _service = VvsipService.getService();
+                if (_service == null)
+                    return;
+                VvsipTask _vvsipTask = _service.getVvsipTask();
+                if (_vvsipTask == null)
+                    return;
+                running = false;
+                Log.i(mTag, "videoout: VideoDisplay removed");
+                _vvsipTask.setvideodisplay(null);
+                if (mIncomingImage != null)
+                    mIncomingImage.recycle();
+                mIncomingImage = null;
+            }
+
+            @SuppressLint("WrongCall")
+            @Override
+            public void surfaceCreated(SurfaceHolder holder) {
+
+                try {
+                    Canvas c = holder.lockCanvas(null);
+                    onDraw(c);
+                    holder.unlockCanvasAndPost(c);
+                } catch (Exception e) {
+
+                }
+
+                IVvsipService _service = VvsipService.getService();
+                if (_service == null)
+                    return;
+                VvsipTask _vvsipTask = _service.getVvsipTask();
+                if (_vvsipTask == null)
+                    return;
+                _vvsipTask.setvideodisplay(VideoDisplay.this);
+                running = true;
+                Log.i(mTag, "videoout: VideoDisplay provided (2)");
+            }
+
+            @Override
+            public void surfaceChanged(SurfaceHolder holder, int format,
+                                       int width, int height) {
+            }
+        });
+    }
+
+    static int gcd(int m, int n) {
+        if (n == 0)
+            return m;
+        else
+            return gcd(n, m % n);
+    }
+
+    int ratiow = 0;
+    int ratioh = 0;
+
+    void reduce(int num, int denom) {
+        int divisor = gcd(num, denom);
+        ratiow = num / divisor;
+        ratioh = denom / divisor;
+    }
+
+    public Bitmap lockIncomingImage(int width, int height) {
+        if (running == false)
+            return null;
+
+        if (mIncomingImage == null || mIncomingImage.getWidth() != width
+                || mIncomingImage.getHeight() != height) {
+            Log.i(mTag, "videoout: Creating bitmap");
+
+            reduce(width, height);
+            Log.i(mTag, "videoout: transform " + width + "x" + height + " ratio " + ratiow + "x" + ratioh);
+            mVideoViewWidth = findViewById(R.id.video_view2).getWidth();
+            mVideoViewHeight = findViewById(R.id.video_view2).getHeight();
+            Log.i(mTag, "videoout: transform " + width + "x" + height + " into " + mVideoViewWidth + "x" + mVideoViewHeight);
+            int wtmp = mVideoViewWidth / ratiow;
+            wtmp = wtmp * ratiow;
+            int htmp = mVideoViewHeight / ratioh;
+            htmp = htmp * ratioh;
+            if (htmp * ratiow > wtmp * ratioh) {
+                htmp = wtmp * ratioh / ratiow;
+            } else {
+                wtmp = htmp * ratiow / ratioh;
+            }
+            Log.i(mTag, "videoout: transform " + width + "x" + height + " final size " + wtmp + "x" + htmp);
+
+            if (wtmp > 3 * width && Build.VERSION.SDK_INT < 11) {
+                //refuse to have such bigger resizing
+                wtmp = width * 3;
+                htmp = height * 3;
+                Log.i(mTag, "videoout: transform " + width + "x" + height + " final size reduced to " + wtmp + "x" + htmp);
+            }
+
+            if (Build.VERSION.SDK_INT <= 8) {
+                //keep good performance on device older than android 2.2
+                onscreen_width = width;
+                onscreen_height = height;
+                if (wtmp > 2 * width) {
+                    //refuse to have such bigger resizing
+                    wtmp = width * 2;
+                    htmp = height * 2;
+                    Log.i(mTag, "videoout: transform " + width + "x" + height + " final size reduced to " + wtmp + "x" + htmp);
+                } else {
+                    //refuse to have such bigger resizing
+                    wtmp = width;
+                    htmp = height;
+                    Log.i(mTag, "videoout: transform " + width + "x" + height + " final size reduced to " + wtmp + "x" + htmp);
+                }
+            }
+
+            try {
+                if (android_scaling == 0) {
+                    onscreen_width = wtmp;
+                    onscreen_height = htmp;
+                    mIncomingImage = Bitmap.createBitmap(width, height, Config.RGB_565);
+                } else {
+                    mIncomingImage = Bitmap.createBitmap(wtmp, htmp, Config.RGB_565);
+                }
+            } catch (Exception e) {
+                mIncomingImage = null;
+                Log.e(mTag, "Bitmap.createBitmap failed -- Exception: " + e.getMessage());
+            }
+        }
+        return mIncomingImage;
+    }
+
+    @SuppressLint("WrongCall")
+    public synchronized void unlockIncomingImage() {
+        try {
+            if (running == false)
+                return;
+            Canvas c = holder.lockCanvas(null);
+            onDraw(c);
+            holder.unlockCanvasAndPost(c);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+
+        if (mStaticImage == null) {
+            canvas.drawColor(Color.BLACK);
+            return;
+        }
+
+        if (mIncomingImage == null) {
+            int posx = (this.getWidth() - mStaticImage.getWidth()) / 2;
+            int posh = (this.getHeight() - mStaticImage.getHeight()) / 2;
+            canvas.drawColor(Color.BLACK);
+            canvas.drawBitmap(mStaticImage, posx, posh, null);
+        } else {
+
+            if (android_scaling == 0) {
+                int posx = (this.getWidth() - onscreen_width) / 2;
+                int post = (this.getHeight() - onscreen_height) / 2;
+                int posr = (this.getWidth() - onscreen_width) / 2 + onscreen_width;
+                int posb = (this.getHeight() - onscreen_height) / 2 + onscreen_height;
+
+                if (orig == null || dest == null) {
+                    orig = new Rect(0, 0, mIncomingImage.getWidth(), mIncomingImage.getHeight());
+                    dest = new Rect(posx, post, posr, posb);
+                } else {
+                    orig.set(0, 0, mIncomingImage.getWidth(), mIncomingImage.getHeight());
+                    dest.set(posx, post, posr, posb);
+                }
+                canvas.drawColor(Color.BLACK);
+                canvas.drawBitmap(mIncomingImage, orig, dest, null);
+            } else {
+                int posx = (this.getWidth() - mIncomingImage.getWidth()) / 2;
+                int post = (this.getHeight() - mIncomingImage.getHeight()) / 2;
+                canvas.drawColor(Color.BLACK);
+                canvas.drawBitmap(mIncomingImage, posx, post, null);
+            }
+        }
+    }
+
+}

BIN
AmDemo_R/src/main/res/drawable-hdpi/ic_call_end_white_48dp.png


BIN
AmDemo_R/src/main/res/drawable-hdpi/ic_launcher.png


BIN
AmDemo_R/src/main/res/drawable-hdpi/ic_videocam_off_white_48dp.png


BIN
AmDemo_R/src/main/res/drawable-mdpi/ic_launcher.png


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 34 - 0
AmDemo_R/src/main/res/drawable-v24/ic_launcher_foreground.xml


BIN
AmDemo_R/src/main/res/drawable-xhdpi/ic_launcher.png


BIN
AmDemo_R/src/main/res/drawable-xxhdpi/ic_launcher.png


+ 170 - 0
AmDemo_R/src/main/res/drawable/ic_launcher_background.xml

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

+ 169 - 0
AmDemo_R/src/main/res/layout/activity_main.xml

@@ -0,0 +1,169 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="com.vvsip.amdemo.MainActivity" >
+
+    <FrameLayout
+        android:id="@+id/content_frame"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <LinearLayout
+        android:id="@+id/linearLayout1"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="vertical" >
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal" >
+
+            <EditText
+                android:id="@+id/server_addr"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:ems="10"
+                android:text="192.168.1.103:5060" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal" >
+
+            <EditText
+                android:id="@+id/userphone"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:ems="10"
+                android:text="1001" >
+
+                <requestFocus />
+            </EditText>
+
+            <EditText
+                android:id="@+id/passwd"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:ems="10"
+                android:text="1234" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal" >
+
+            <Button
+                android:id="@+id/register"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="注册" />
+
+        </LinearLayout>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/linearLayout1"
+        android:layout_weight="1"
+        android:orientation="vertical" >
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal" >
+
+            <EditText
+                android:id="@+id/callee"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="6"
+                android:text="1002" />
+
+            <Button
+                android:id="@+id/audio_call"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="打电话" />
+
+            <Button
+                android:id="@+id/add_video"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="视频" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal" >
+
+            <Button
+                android:id="@+id/end_call"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="结束通话" />
+
+            <Button
+                android:id="@+id/off_hook"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="接电话" />
+            
+            
+            
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal" >
+
+        <EditText
+            android:id="@+id/dtmf"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="6"
+            android:text="1" />
+
+        <Button
+                android:id="@+id/send_dtmf"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="发送DTMF" 
+                android:layout_weight="1"/>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal" >
+
+            <ScrollView
+                android:id="@+id/scrollView1"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent" >
+
+                <TextView
+                    android:id="@+id/call_status"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="..." />
+            </ScrollView>
+        </LinearLayout>
+    </LinearLayout>
+
+</RelativeLayout>

+ 9 - 0
AmDemo_R/src/main/res/layout/activity_main3.xml

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

+ 85 - 0
AmDemo_R/src/main/res/layout/splash_layout.xml

@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:layout_gravity="center"
+    android:orientation="vertical" >
+
+    <LinearLayout
+        android:id="@+id/LinearLayout03"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_weight="1"
+        android:orientation="horizontal" >
+
+        <ImageView
+            android:id="@+id/ImageView01"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|center"
+            android:contentDescription="www.vvsip.com"
+            android:src="@drawable/ic_launcher" >
+        </ImageView>
+
+        <LinearLayout
+            android:id="@+id/LinearLayout01"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom|center"
+            android:orientation="vertical" >
+
+            <TextView
+                android:id="@+id/TextView01"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/app_name" >
+            </TextView>
+
+            <TextView
+                android:id="@+id/TextView_version"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:text="customized by vvsip" >
+            </TextView>
+        </LinearLayout>
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/LinearLayout05"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginTop="40dp"
+        android:layout_weight="1"
+        android:orientation="vertical" >
+
+        <TextView
+            android:id="@+id/my_version"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="end"
+            android:text="V2.0:www.vvsip.com\nV1.0:www.vv-net.com" >
+        </TextView>
+
+        <TextView
+            android:id="@+id/TextView_link"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="end"
+            android:text="QQ272108638"
+            android:visibility="gone" >
+        </TextView>
+
+        <TextView
+            android:id="@+id/TextView_licencelink"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="end"
+            android:text="copyright by vvsip"
+            android:visibility="gone" >
+        </TextView>
+    </LinearLayout>
+
+</LinearLayout>

+ 200 - 0
AmDemo_R/src/main/res/layout/video_camera.xml

@@ -0,0 +1,200 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/TopLevelView"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+
+    <com.vvsip.viewsip.VideoDisplay
+        android:id="@+id/video_view2"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"/>
+
+    <com.vvsip.ansip.video.VideoCameraPreview
+        android:id="@+id/camera_preview2"
+        android:layout_width="160dp"
+        android:layout_height="120dp"
+        android:layout_gravity="bottom|end"
+        android:layout_marginBottom="50dp"
+        android:layout_marginLeft="3dp"
+        android:layout_marginRight="3dp" >
+    </com.vvsip.ansip.video.VideoCameraPreview>
+
+    <LinearLayout
+        android:id="@+id/ButtonRow"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom"
+        android:gravity="center" >
+
+        <!-- HANG BUTTON -->
+
+        <ImageButton
+            android:id="@+id/Button_hang"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:layout_marginLeft="3dp"
+            android:layout_marginRight="3dp"
+            android:layout_weight="1"
+            android:background="#f22121"
+            android:clickable="true"
+            android:contentDescription="挂断"
+            android:focusable="false"
+            android:minHeight="56dp"
+            android:src="@drawable/ic_call_end_white_48dp" />
+
+        <!-- STOP VIDEO BUTTON -->
+
+        <ImageButton
+            android:id="@+id/Button_stopvideo"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:layout_marginLeft="3dp"
+            android:layout_marginRight="3dp"
+            android:layout_weight="1"
+            android:background="#f22121"
+            android:clickable="true"
+            android:contentDescription="previous"
+            android:focusable="false"
+            android:minHeight="56dp"
+            android:src="@drawable/ic_videocam_off_white_48dp" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/ButtonStatistics"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        android:gravity="center"
+        android:orientation="vertical" >
+
+        <LinearLayout
+            android:id="@+id/LinearLayoutAudio"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical|center_horizontal" >
+
+            <TextView
+                android:id="@+id/TextView_uploadrate"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|center_horizontal"
+                android:layout_marginRight="10dp"
+                android:layout_marginEnd="10dp"
+                android:textColor="#ffff4444"
+                android:text="up_kbps"
+                android:textSize="12sp"
+                android:visibility="gone" >
+            </TextView>
+
+            <TextView
+                android:id="@+id/TextView_downloadrate"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|center_horizontal"
+                android:layout_marginRight="10dp"
+                android:layout_marginEnd="10dp"
+                android:textColor="#ffff4444"
+                android:text="down_kbps"
+                android:textSize="12sp"
+                android:visibility="gone" >
+            </TextView>
+
+            <TextView
+                android:id="@+id/TextView_lossrate"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|center_horizontal"
+                android:layout_marginRight="10dp"
+                android:layout_marginEnd="10dp"
+                android:textColor="#ffff4444"
+                android:text="loss_percent"
+                android:textSize="12sp"
+                android:visibility="gone" >
+            </TextView>
+
+            <TextView
+                android:id="@+id/TextView_remotelossrate"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|center_horizontal"
+                android:textColor="#ffff4444"
+                android:text="loss_percent"
+                android:textSize="12sp"
+                android:visibility="gone" >
+            </TextView>
+            </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/LinearLayoutVideo"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical|center_horizontal" >
+
+            <TextView
+                android:id="@+id/TextView_videouploadrate"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|center_horizontal"
+                android:layout_marginRight="10dp"
+                android:layout_marginEnd="10dp"
+                android:textColor="#ffff4444"
+                android:text="up_kbps"
+                android:textSize="12sp"
+                android:visibility="gone" >
+            </TextView>
+
+            <TextView
+                android:id="@+id/TextView_videodownloadrate"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|center_horizontal"
+                android:layout_marginRight="10dp"
+                android:layout_marginEnd="10dp"
+                android:textColor="#ffff4444"
+                android:text="down_kbps"
+                android:textSize="12sp"
+                android:visibility="gone" >
+            </TextView>
+
+            <TextView
+                android:id="@+id/TextView_videolossrate"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|center_horizontal"
+                android:layout_marginRight="10dp"
+                android:layout_marginEnd="10dp"
+                android:textColor="#ffff4444"
+                android:text="loss_percent"
+                android:textSize="12sp"
+                android:visibility="gone" >
+            </TextView>
+
+            <TextView
+                android:id="@+id/TextView_videoremotelossrate"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical|center_horizontal"
+                android:textColor="#ffff4444"
+                android:text="loss_percent"
+                android:textSize="12sp"
+                android:visibility="gone" >
+            </TextView>
+            </LinearLayout>
+    </LinearLayout>
+
+</FrameLayout>

+ 5 - 0
AmDemo_R/src/main/res/mipmap-anydpi-v26/ic_launcher.xml

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

+ 5 - 0
AmDemo_R/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml

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

BIN
AmDemo_R/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
AmDemo_R/src/main/res/mipmap-hdpi/ic_launcher_round.png


BIN
AmDemo_R/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
AmDemo_R/src/main/res/mipmap-mdpi/ic_launcher_round.png


BIN
AmDemo_R/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
AmDemo_R/src/main/res/mipmap-xhdpi/ic_launcher_round.png


BIN
AmDemo_R/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
AmDemo_R/src/main/res/mipmap-xxhdpi/ic_launcher_round.png


BIN
AmDemo_R/src/main/res/mipmap-xxxhdpi/ic_launcher.png


BIN
AmDemo_R/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png


BIN
AmDemo_R/src/main/res/raw/holdmusic.wav


BIN
AmDemo_R/src/main/res/raw/ringback.wav


+ 6 - 0
AmDemo_R/src/main/res/values/colors.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#008577</color>
+    <color name="colorPrimaryDark">#00574B</color>
+    <color name="colorAccent">#D81B60</color>
+</resources>

+ 87 - 0
AmDemo_R/src/main/res/values/preference_values.xml

@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <string-array name="listProtocolDisplay" translatable="false">
+        <item>UDP</item>
+        <item>TCP</item>
+        <item>TLS</item>
+    </string-array>
+    <string-array name="listProtocolValue" translatable="false">
+        <item>UDP</item>
+        <item>TCP</item>
+        <item>TLS</item>
+    </string-array>
+    <string-array name="listCameraFps" translatable="false">
+        <item>5</item>
+        <item>10</item>
+        <item>15</item>
+        <item>20</item>
+    </string-array>
+    <string-array name="listCameraFpsValue" translatable="false">
+        <item>5</item>
+        <item>10</item>
+        <item>15</item>
+        <item>20</item>
+    </string-array>
+    <string-array name="listCameraSize" translatable="false">
+        <item>default</item>
+        <item>small</item>
+        <item>normal</item>
+        <item>large</item>
+        <item>HD</item>
+    </string-array>
+    <string-array name="listCameraSizeValue" translatable="false">
+        <item>default</item>
+        <item>small</item>
+        <item>normal</item>
+        <item>large</item>
+        <item>HD</item>
+    </string-array>
+    <string-array name="listVideoUploadRate" translatable="false">
+        <item>64</item>
+        <item>128</item>
+        <item>256</item>
+        <item>512</item>
+        <item>1024</item>
+    </string-array>
+    <string-array name="listVideoUploadRateValue" translatable="false">
+        <item>64</item>
+        <item>128</item>
+        <item>256</item>
+        <item>512</item>
+        <item>1024</item>
+    </string-array>
+    <string-array name="listVideoDownloadRate" translatable="false">
+        <item>64</item>
+        <item>128</item>
+        <item>256</item>
+        <item>512</item>
+        <item>1024</item>
+    </string-array>
+    <string-array name="listVideoDownloadRateValue" translatable="false">
+        <item>64</item>
+        <item>128</item>
+        <item>256</item>
+        <item>512</item>
+        <item>1024</item>
+    </string-array>
+    <string-array name="listEncryptionDisplay" translatable="false">
+        <item>RTP</item>
+        <item>optional SRTP</item>
+        <item>SRTP</item>
+    </string-array>
+    <string-array name="listEncryptionValue" translatable="false">
+        <item>RTP</item>
+        <item>optional SRTP</item>
+        <item>SRTP</item>
+    </string-array>
+    <string-array name="listAudioProcessingAlternative" translatable="false">
+        <item>0</item>
+        <item>1</item>
+    </string-array>
+    <string-array name="listAudioProcessingAlternativeValue" translatable="false">
+        <item>webrtc audio processing</item>
+        <item>speex audio processing</item>
+    </string-array>
+    
+</resources>

+ 71 - 0
AmDemo_R/src/main/res/values/strings.xml

@@ -0,0 +1,71 @@
+<resources>
+    <string name="app_name">AmDemo_R</string>
+    <string name="hello_world">Hello world!</string>
+    <string name="action_settings">Settings</string>
+    <string name="advanced_pref">Advanced Preferences</string>
+    <string name="pref_audio_advanced">Audio Options</string>
+    <string name="pref_audio_fastoutputsetup">Fast Input Setup</string>
+    <string name="pref_audio_fastinputsetup">Fast Output Setup</string>
+    <string name="pref_videocodec_advanced">Video Options</string>
+    <string name="pref_videocodec_h264_hw_decoder">HW H264 Decoder</string>
+    <string name="pref_videocodec_h264_hw_encoder">HW H264 Encoder</string>
+    <string name="pref_video_disablecamera">Disable Camera</string>
+    <string name="pref_echo_limiter">Echo Limitation</string>
+    <string name="pref_threshold">Threshold (0.02)</string>
+    <string name="pref_speed">Speed (0.02)</string>
+    <string name="pref_force">Force (50.0)</string>
+    <string name="pref_sustain">Sustain (300ms)</string>
+    <string name="pref_apm_alternative">Audio Module</string>
+    <string name="pref_agc">AGC</string>
+    <string name="pref_highpassfilter">High Pass Filter</string>
+    <string name="pref_noisesuppression">Noise Suppression</string>
+
+    <string name="pref_audiocodec_g729" translatable="false">G729</string>
+    <string name="pref_audiocodec_silkwb" translatable="false">SILK (16Khz)</string>
+    <string name="pref_audiocodec_opus" translatable="false">OPUS</string>
+    <string name="pref_audiocodec_isac16" translatable="false">iSAC (16Khz)</string>
+    <string name="pref_audiocodec_speex16" translatable="false">speex (16KHz)</string>
+    <string name="pref_audiocodec_g722" translatable="false">g722 (16Khz)</string>
+    <string name="pref_audiocodec_silknb" translatable="false">SILK (8Khz)</string>
+    <string name="pref_audiocodec_speex8" translatable="false">speex (8KHz)</string>
+    <string name="pref_audiocodec_ilbc" translatable="false">iLBC (30ms)</string>
+    <string name="pref_audiocodec_gsm" translatable="false">GSM</string>
+    <string name="pref_audiocodec_pcmu" translatable="false">PCMU</string>
+    <string name="pref_audiocodec_pcma" translatable="false">PCMA</string>
+    <string name="pref_videocodec_vp8" translatable="false">VP8</string>
+    <string name="pref_videocodec_h264" translatable="false">H264</string>
+    <string name="pref_videocodec_h2631998" translatable="false">H263&#8211;1998</string>
+    <string name="pref_videocodec_h263" translatable="false">H263</string>
+    <string name="pref_videocodec_mp4v" translatable="false">MP4V&#8211;ES</string>
+
+    <string name="pref_domain">Domain</string>
+    <string name="pref_username">Username</string>
+    <string name="pref_password">Password</string>
+    <string name="pref_optional_settings">Extra SIP Settings</string>
+    <string name="pref_ringer_duration">Ringer Duration</string>
+    <string name="pref_local_sip_port">Local SIP Port</string>
+    <string name="pref_rtp_port_range">RTP Port range</string>
+    <string name="pref_sip_identity">SIP Identity</string>
+    <string name="pref_outboundproxy">SIP Outbound Proxy</string>
+    <string name="pref_stunserver">STUN Server</string>
+    <string name="pref_transportlayer">Protocol</string>
+    <string name="pref_interval">refresh interval</string>
+    <string name="pref_autostart">Start On Boot</string>
+    <string name="pref_audio_parameters">Audio Parameters</string>
+    <string name="pref_echocancellation">Echo Cancellation</string>
+    <string name="pref_haveearpiecemode">Smartphone (vs tablet)?</string>
+    <string name="pref_apminearpiecemode">Audio Module in earpiece mode</string>
+    <string name="pref_audiocodec">Audio Codecs</string>
+    <string name="pref_audiogain">Audio Gains</string>
+    <string name="pref_outputgain_speakeroff">Output</string>
+    <string name="pref_inputgain_speakeroff">Input</string>
+    <string name="pref_outputgain_speakeron">Output (Speaker Mode)</string>
+    <string name="pref_inputgain_speakeron">Input (Speaker Mode)</string>
+    <string name="pref_video_parameters">Video Parameters</string>
+    <string name="pref_video_fps">Frame per second (fps)</string>
+    <string name="pref_video_size">Image Size</string>
+    <string name="pref_videouploadrate">Max Upload Rate</string>
+    <string name="pref_videodownloadrate">Max Download Rate</string>
+    <string name="pref_videocodec">Video Codec</string>
+    <string name="pref_encryption">encryption</string>
+</resources>

+ 11 - 0
AmDemo_R/src/main/res/values/styles.xml

@@ -0,0 +1,11 @@
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+        <item name="colorAccent">@color/colorAccent</item>
+    </style>
+
+</resources>

+ 17 - 0
AmDemo_R/src/main/res/xml/advanced_pref.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen
+  xmlns:android="http://schemas.android.com/apk/res/android">
+
+<PreferenceCategory android:title="@string/advanced_pref">
+<PreferenceScreen android:title="@string/pref_audio_advanced" android:key="key_audio_advanced">
+    <CheckBoxPreference android:title="@string/pref_audio_fastoutputsetup" android:key="key_audio_fastoutputsetup" android:defaultValue="true"></CheckBoxPreference>
+    <CheckBoxPreference android:title="@string/pref_audio_fastinputsetup" android:key="key_audio_fastinputsetup" android:defaultValue="false"></CheckBoxPreference>
+</PreferenceScreen>
+
+<PreferenceScreen android:title="@string/pref_videocodec_advanced" android:key="key_videocodec_advanced">
+    <CheckBoxPreference android:title="@string/pref_videocodec_h264_hw_encoder" android:key="key_videocodec_h264_hw_encoder" android:defaultValue="false"></CheckBoxPreference>
+    <CheckBoxPreference android:title="@string/pref_videocodec_h264_hw_decoder" android:key="key_videocodec_h264_hw_decoder" android:defaultValue="false"></CheckBoxPreference>
+    <CheckBoxPreference android:title="@string/pref_video_disablecamera" android:key="key_video_disablecamera" android:defaultValue="false"></CheckBoxPreference>
+</PreferenceScreen>
+</PreferenceCategory> 
+</PreferenceScreen>

+ 5 - 0
AmDemo_R/src/main/res/xml/pref_media_advanced_audio.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <CheckBoxPreference android:title="@string/pref_audio_fastoutputsetup" android:key="key_audio_fastoutputsetup" android:defaultValue="true"></CheckBoxPreference>
+    <CheckBoxPreference android:title="@string/pref_audio_fastinputsetup" android:key="key_audio_fastinputsetup" android:defaultValue="false"></CheckBoxPreference>
+</PreferenceScreen>

+ 7 - 0
AmDemo_R/src/main/res/xml/pref_media_advanced_video.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+    android:key="key_videocodec_advanced">
+    <CheckBoxPreference android:title="@string/pref_videocodec_h264_hw_encoder" android:key="key_videocodec_h264_hw_encoder" android:defaultValue="false"></CheckBoxPreference>
+    <CheckBoxPreference android:title="@string/pref_videocodec_h264_hw_decoder" android:key="key_videocodec_h264_hw_decoder" android:defaultValue="false"></CheckBoxPreference>
+    <CheckBoxPreference android:title="@string/pref_video_disablecamera" android:key="key_video_disablecamera" android:defaultValue="false"></CheckBoxPreference>
+</PreferenceScreen>

+ 167 - 0
AmDemo_R/src/main/res/xml/pref_media_audio.xml

@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <ListPreference
+        android:defaultValue="0"
+        android:entries="@array/listAudioProcessingAlternativeValue"
+        android:entryValues="@array/listAudioProcessingAlternative"
+        android:key="key_apm_alternative"
+        android:title="@string/pref_apm_alternative" >
+    </ListPreference>
+
+    <CheckBoxPreference
+        android:defaultValue="true"
+        android:key="key_agc"
+        android:title="@string/pref_agc" >
+    </CheckBoxPreference>
+    <CheckBoxPreference
+        android:defaultValue="true"
+        android:key="key_noisesuppression"
+        android:title="@string/pref_noisesuppression" >
+    </CheckBoxPreference>
+    <CheckBoxPreference
+        android:defaultValue="true"
+        android:key="key_highpassfilter"
+        android:title="@string/pref_highpassfilter" >
+    </CheckBoxPreference>
+    <CheckBoxPreference
+        android:defaultValue="true"
+        android:key="key_echocancellation"
+        android:title="@string/pref_echocancellation" >
+    </CheckBoxPreference>
+
+    <PreferenceScreen
+        android:key="key_ps_echo_limiter"
+        android:title="@string/pref_echo_limiter" >
+        <CheckBoxPreference
+            android:defaultValue="true"
+            android:key="key_echo_limiter"
+            android:title="@string/pref_echo_limiter" >
+        </CheckBoxPreference>
+
+        <EditTextPreference
+            android:defaultValue="0.02"
+            android:key="key_threshold"
+            android:numeric="decimal"
+            android:title="@string/pref_threshold" >
+        </EditTextPreference>
+        <EditTextPreference
+            android:defaultValue="0.02"
+            android:key="key_speed"
+            android:numeric="decimal"
+            android:title="@string/pref_speed" >
+        </EditTextPreference>
+        <EditTextPreference
+            android:defaultValue="50.0"
+            android:key="key_force"
+            android:numeric="decimal"
+            android:title="@string/pref_force" >
+        </EditTextPreference>
+        <EditTextPreference
+            android:defaultValue="300"
+            android:key="key_sustain"
+            android:numeric="integer"
+            android:title="@string/pref_sustain" >
+        </EditTextPreference>
+    </PreferenceScreen>
+
+    <CheckBoxPreference
+        android:key="key_haveearpiecemode"
+        android:title="@string/pref_haveearpiecemode" >
+    </CheckBoxPreference>
+    <CheckBoxPreference
+        android:defaultValue="false"
+        android:key="key_apm_in_earpiecemode"
+        android:title="@string/pref_apminearpiecemode" >
+    </CheckBoxPreference>
+
+    <PreferenceScreen android:title="@string/pref_audiogain" >
+        <EditTextPreference
+            android:defaultValue="1.0"
+            android:key="key_outputgain_speakeroff"
+            android:numeric="decimal"
+            android:title="@string/pref_outputgain_speakeroff" >
+        </EditTextPreference>
+        <EditTextPreference
+            android:defaultValue="1.0"
+            android:key="key_inputgain_speakeroff"
+            android:numeric="decimal"
+            android:title="@string/pref_inputgain_speakeroff" >
+        </EditTextPreference>
+        <EditTextPreference
+            android:defaultValue="1.0"
+            android:key="key_outputgain_speakeron"
+            android:numeric="decimal"
+            android:title="@string/pref_outputgain_speakeron" >
+        </EditTextPreference>
+        <EditTextPreference
+            android:defaultValue="1.0"
+            android:key="key_inputgain_speakeron"
+            android:numeric="decimal"
+            android:title="@string/pref_inputgain_speakeron" >
+        </EditTextPreference>
+    </PreferenceScreen>
+    <PreferenceScreen android:title="@string/pref_audiocodec" >
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="key_audiocodec_g729"
+            android:title="@string/pref_audiocodec_g729" >
+        </CheckBoxPreference>
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="key_audiocodec_silknb"
+            android:title="@string/pref_audiocodec_silknb" >
+        </CheckBoxPreference>
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="key_audiocodec_ilbc"
+            android:title="@string/pref_audiocodec_ilbc" >
+        </CheckBoxPreference>
+        <CheckBoxPreference
+            android:defaultValue="true"
+            android:key="key_audiocodec_pcmu"
+            android:title="@string/pref_audiocodec_pcmu" >
+        </CheckBoxPreference>
+        <CheckBoxPreference
+            android:defaultValue="true"
+            android:key="key_audiocodec_pcma"
+            android:title="@string/pref_audiocodec_pcma" >
+        </CheckBoxPreference>
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="key_audiocodec_silkwb"
+            android:title="@string/pref_audiocodec_silkwb" >
+        </CheckBoxPreference>
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="key_audiocodec_opus"
+            android:title="@string/pref_audiocodec_opus" >
+        </CheckBoxPreference>
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="key_audiocodec_isac16"
+            android:title="@string/pref_audiocodec_isac16" >
+        </CheckBoxPreference>
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="key_audiocodec_speex16"
+            android:title="@string/pref_audiocodec_speex16" >
+        </CheckBoxPreference>
+        <CheckBoxPreference
+            android:defaultValue="true"
+            android:key="key_audiocodec_g722"
+            android:title="@string/pref_audiocodec_g722" >
+        </CheckBoxPreference>
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="key_audiocodec_speex8"
+            android:title="@string/pref_audiocodec_speex8" >
+        </CheckBoxPreference>
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="key_audiocodec_gsm"
+            android:title="@string/pref_audiocodec_gsm" >
+        </CheckBoxPreference>
+    </PreferenceScreen>
+
+</PreferenceScreen>

+ 62 - 0
AmDemo_R/src/main/res/xml/pref_media_video.xml

@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <ListPreference
+        android:defaultValue="5"
+        android:entries="@array/listCameraFpsValue"
+        android:entryValues="@array/listCameraFps"
+        android:key="key_video_fps"
+        android:title="@string/pref_video_fps" >
+    </ListPreference>
+    <ListPreference
+        android:defaultValue="normal"
+        android:entries="@array/listCameraSizeValue"
+        android:entryValues="@array/listCameraSize"
+        android:key="key_video_size"
+        android:title="@string/pref_video_size" >
+    </ListPreference>
+    <ListPreference
+        android:defaultValue="512"
+        android:entries="@array/listVideoUploadRateValue"
+        android:entryValues="@array/listVideoUploadRate"
+        android:key="key_videouploadrate"
+        android:title="@string/pref_videouploadrate" >
+    </ListPreference>
+    <ListPreference
+        android:defaultValue="512"
+        android:entries="@array/listVideoDownloadRateValue"
+        android:entryValues="@array/listVideoDownloadRate"
+        android:key="key_videodownloadrate"
+        android:title="@string/pref_videodownloadrate" >
+    </ListPreference>
+
+    <PreferenceScreen android:title="@string/pref_videocodec" >
+        <CheckBoxPreference
+            android:defaultValue="true"
+            android:key="key_videocodec_h264"
+            android:title="@string/pref_videocodec_h264" >
+        </CheckBoxPreference>
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="key_videocodec_vp8"
+            android:title="@string/pref_videocodec_vp8" >
+        </CheckBoxPreference>
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="key_videocodec_mp4v"
+            android:title="@string/pref_videocodec_mp4v" >
+        </CheckBoxPreference>
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="key_videocodec_h2631998"
+            android:title="@string/pref_videocodec_h2631998" >
+        </CheckBoxPreference>
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="key_videocodec_h263"
+            android:title="@string/pref_videocodec_h263" >
+        </CheckBoxPreference>
+        
+    </PreferenceScreen>
+
+</PreferenceScreen>

+ 21 - 0
AmDemo_R/src/main/res/xml/pref_sip_account.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <EditTextPreference
+        android:inputType="textNoSuggestions"
+        android:key="key_domain"
+        android:title="@string/pref_domain" >
+    </EditTextPreference>
+    <EditTextPreference
+        android:inputType="textNoSuggestions"
+        android:key="key_username"
+        android:title="@string/pref_username" >
+    </EditTextPreference>
+    <EditTextPreference
+        android:inputType="textPassword"
+        android:key="key_password"
+        android:password="true"
+        android:title="@string/pref_password" >
+    </EditTextPreference>
+
+</PreferenceScreen>

+ 66 - 0
AmDemo_R/src/main/res/xml/pref_sip_optional.xml

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <EditTextPreference
+        android:defaultValue="40"
+        android:key="key_ringer_duration"
+        android:numeric="integer"
+        android:title="@string/pref_ringer_duration" >
+    </EditTextPreference>
+    <EditTextPreference
+        android:defaultValue="0"
+        android:key="key_local_sip_port"
+        android:numeric="integer"
+        android:title="@string/pref_local_sip_port" >
+    </EditTextPreference>
+    <EditTextPreference
+        android:defaultValue="40100"
+        android:key="key_rtp_port_range"
+        android:numeric="integer"
+        android:title="@string/pref_rtp_port_range" >
+    </EditTextPreference>
+    <EditTextPreference
+        android:inputType="textNoSuggestions"
+        android:key="key_outboundproxy"
+        android:title="@string/pref_outboundproxy" >
+    </EditTextPreference>
+    <EditTextPreference
+        android:inputType="textNoSuggestions"
+        android:key="key_stunserver"
+        android:title="@string/pref_stunserver" >
+    </EditTextPreference>
+
+    <ListPreference
+        android:defaultValue="UDP"
+        android:entries="@array/listProtocolDisplay"
+        android:entryValues="@array/listProtocolValue"
+        android:key="key_protocol"
+        android:title="@string/pref_transportlayer" >
+    </ListPreference>
+
+    <EditTextPreference
+        android:defaultValue="600"
+        android:key="key_interval"
+        android:numeric="integer"
+        android:title="@string/pref_interval" >
+    </EditTextPreference>
+    <EditTextPreference
+        android:inputType="textNoSuggestions"
+        android:key="key_identity"
+        android:title="@string/pref_sip_identity" >
+    </EditTextPreference>
+
+    <CheckBoxPreference
+        android:key="key_autostart"
+        android:title="@string/pref_autostart" >
+    </CheckBoxPreference>
+
+    <ListPreference
+        android:defaultValue="RTP"
+        android:entries="@array/listEncryptionDisplay"
+        android:entryValues="@array/listEncryptionValue"
+        android:key="key_encryption"
+        android:title="@string/pref_encryption" >
+    </ListPreference>
+
+</PreferenceScreen>

+ 17 - 0
AmDemo_R/src/test/java/com/example/amdemo_r/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package com.example.amdemo_r;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}

+ 2 - 2
home/build.gradle

@@ -62,13 +62,12 @@ android {
 //获取编译日期
 String getDate() {
     Date date = new Date();
-    String dates = "\""+date.format("yyyy年MM月dd日", TimeZone.getTimeZone("UTC"))+"\"";
+    String dates = "\"" + date.format("yyyy年MM月dd日", TimeZone.getTimeZone("UTC")) + "\"";
     return dates;
 }
 
 
 dependencies {
-    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
     compile fileTree(dir: 'libs', include: ['*.jar'])
     androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
         exclude group: 'com.android.support', module: 'support-annotations'
@@ -119,6 +118,7 @@ dependencies {
     implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
 
     compile project(':starRTC')
+    compile project(':AmDemo_R')
 }
 
 /**

+ 4 - 1
home/src/main/AndroidManifest.xml

@@ -9,7 +9,7 @@
         android:allowBackup="true"
         android:label="@string/app_name"
         android:supportsRtl="true">
-        <activity android:name=".SOSEmergencyCallActivity"></activity>
+
         <activity
             android:name=".activity.HomeActivity"
             android:launchMode="singleTask" />
@@ -21,6 +21,9 @@
         <activity android:name=".activity.EventActivity" />
         <activity android:name=".activity.WatchEventDetailActivity" />
         <activity android:name=".activity.WatchUserSettingActivity" />
+        <activity android:name=".SOSEmergencyCallActivity" />
+        <activity android:name=".activity.SipVoipAudioRingingActivity"></activity>
+        <activity android:name=".activity.SipVoipAudioActivity" />
 
         <service android:name="com.starrtc.demo.demo.service.KeepLiveService">
             <intent-filter>

+ 16 - 0
home/src/main/code/com/wdkl/ncs/android/component/home/SipUtil/SipCallBack.java

@@ -0,0 +1,16 @@
+package com.wdkl.ncs.android.component.home.SipUtil;
+
+/**
+ * ======================Sip回调接口=====================
+ * Created by dengzhe on 2018/2/7.
+ */
+
+public interface SipCallBack {
+    void startCall(String sipAddress);//开始拨打
+
+    void autoTalking();//自动接听
+
+    void endCall();//结束通话
+
+
+}

+ 626 - 0
home/src/main/code/com/wdkl/ncs/android/component/home/SipUtil/SipHelper.java

@@ -0,0 +1,626 @@
+package com.wdkl.ncs.android.component.home.SipUtil;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.CountDownTimer;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.util.Log;
+
+import com.vvsip.ansip.IVvsipService;
+import com.vvsip.ansip.IVvsipServiceListener;
+import com.vvsip.ansip.VvsipCall;
+import com.vvsip.ansip.VvsipService;
+import com.vvsip.ansip.VvsipServiceBinder;
+import com.vvsip.ansip.VvsipTask;
+import com.wdkl.ncs.android.middleware.utils.MessageEvent;
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.vvsip.ansip.VvsipTask.EXOSIP_CALL_CLOSED;
+
+
+public class SipHelper {
+   private String TAG = SipHelper.class.getSimpleName();
+
+    private final static String SIP_IP_END = ":5060";
+
+    private static String sipIP = "";
+    private static String sipID = "";
+    private static String sipPWD = "";
+
+
+    /**
+     * 注册中
+     */
+    public static final String REGISTERING = "register_ing";
+    /**
+     * 注册失败
+     */
+    public static final String REGISTERFAIL = "register_fail";
+    /**
+     * 注册完成
+     */
+    public static final String REGISTERCOM = "register_com";
+
+    /**
+     * Sip注册状态
+     */
+    public static final int EVENT_SIP_REGISTER_STATUS = 0x01;
+
+
+    /**
+     * Sip启动注册.
+     */
+    protected int mSipRegisterTime = 5000;
+    private Handler sipRegisterHandler = null;
+    private Runnable sipRegisterRunnable = null;
+
+    /**
+     * SIP信息
+     */
+    public static final String SipInfoTag = "SipInfo";
+    /**
+     * 电话呼叫对象
+     */
+    private List<VvsipCall> mVvsipCalls = null;
+
+
+    private static SipHelper sipHelper;
+
+    public Handler getSipRegisterHandler() {
+        return sipRegisterHandler;
+    }
+
+    public Runnable getSipRegisterRunnable() {
+        return sipRegisterRunnable;
+    }
+
+    public List<VvsipCall> getmVvsipCalls() {
+        return mVvsipCalls;
+    }
+
+    private IVvsipServiceListener sipListner;
+
+
+    private Context mContext;
+
+    public static SipHelper getInstance() {
+        if (sipHelper == null) {
+            synchronized (SipHelper.class) {
+                if (sipHelper == null) {
+                    sipHelper = new SipHelper();
+                }
+            }
+        }
+        return sipHelper;
+    }
+
+    /**
+     * Instantiates a new Sip register util.
+     */
+    private SipHelper() {
+    }
+
+    public void initSip(Context context, String ip, String id, String pwd) {
+        mContext = context;
+        sipIP = ip;
+        sipID = id;
+        sipPWD = pwd;
+
+        if (mVvsipCalls == null) {
+            mVvsipCalls = new ArrayList<VvsipCall>();
+        }
+
+        // Runnable exiting the splash screen and launching the menu
+        sipRegisterRunnable = new Runnable() {
+            public void run() {
+                isSuccessRegisterSip();
+            }
+        };
+
+        // Run the exitRunnable in in mSipRegisterTime ms
+        sipRegisterHandler = new Handler();
+
+        IVvsipService sipservice = VvsipService.getService();
+        if (sipservice != null) {
+            sipRegisterHandler.postDelayed(sipRegisterRunnable, 3000);
+            return;
+        }
+        sipRegisterHandler.postDelayed(sipRegisterRunnable, mSipRegisterTime);
+    }
+
+    public void setSipListner(IVvsipServiceListener listner) {
+        sipListner = listner;
+        IVvsipService sipService = VvsipService.getService();
+        if (sipService != null && sipListner != null) {
+            sipService.clearListener();
+            sipService.addListener(sipListner);
+        }
+    }
+
+    /*public void addSipListner(IVvsipServiceListener listner) {
+        IVvsipService sipService = VvsipService.getService();
+        if (sipService != null && listner != null) {
+            sipService.addListener(listner);
+            Log.d("sip", "add sip listner");
+        }
+    }
+
+    public void removeSipListner(IVvsipServiceListener listner) {
+        IVvsipService sipService = VvsipService.getService();
+        if (sipService != null && listner != null) {
+            sipService.removeListener(listner);
+            Log.d("sip", "remove sip listner");
+        }
+    }*/
+
+    /**
+     * 检测Sip服务是否注册成功
+     */
+    public void isSuccessRegisterSip() {
+        VvsipTask vvsipTask = VvsipTask.getVvsipTask();
+        if (vvsipTask != null && VvsipTask.global_failure != 0) {
+            /**
+             * ==================================sip服务启动失败 ================================
+             */
+        }
+    }
+
+    /**
+     * 注销Sip服务
+     */
+    public void unRegisterSip() {
+        //LogUtil.i(SipInfoTag, "lifecycle // onDestroy");
+
+        IVvsipService sipservice = VvsipService.getService();
+        /*if (sipservice != null && sipListner != null) {
+            sipservice.removeListener(sipListner);
+        }*/
+        if (sipservice != null) {
+            sipservice.clearListener();
+        }
+
+        getSipServiceStartHandler().removeCallbacks(getSipServiceStartRunnable());
+        sipRegisterHandler.removeCallbacks(sipRegisterRunnable);
+        if (getSipServiceConnection() != null && isRegister) {
+            try {
+                mContext.unbindService(getSipServiceConnection());
+                setSipServiceConnection(null);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        if (mVvsipCalls != null) {
+            mVvsipCalls.clear();
+            mVvsipCalls = null;
+        }
+
+        //Log.i(SipInfoTag, "lifecycle // onDestroy");
+    }
+
+    public static String sipStatus = "";
+
+    /**
+     * Sip信息获取
+     */
+    public void obtainSipInfo() {
+        if (sipStatus.equals(REGISTERCOM) /*&& NetHelper.NetConn*/) {//sip注册成功并且以太网连上
+            return;
+        }
+        IVvsipService sipService = VvsipService.getService();
+        if (sipService != null) {
+            sipService.setMessageHandler(messageHandler);
+            if (sipListner != null) {
+                sipService.clearListener();
+                sipService.addListener(sipListner);
+            }
+        }
+
+        sipRegister();
+        //failUiRefreshSip();
+    }
+
+//    private void failUiRefreshSip() {
+//        if (!NetHelper.NetConn) {
+//            sipStatus = REGISTERFAIL;
+//            EventBus.getDefault().post(new MessageEvent(REGISTERFAIL, EVENT_SIP_REGISTER_STATUS));
+//            if (mSipThread != null) {
+//                mSipThread.interrupt();
+//                mSipThread = null;
+//            }
+//            //LogUtil.e(SipInfoTag, "以太网断开,SIP UI状态刷新为失败");
+//        }
+//    }
+
+
+    /**
+     * Sip信息
+     */
+    private String sipinfo = "";
+    private static int handleCount = 0;
+    //Sip註冊次數
+    private CountDownTimer mCountDownAutoTimer;
+    @SuppressLint("HandlerLeak")
+    private Handler messageHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            //LogUtil.i("QASE", "handleMessage==" + " msg.obj==" + msg.obj.toString() + " msg.what==" + msg.what);
+            //LogUtil.i(SipInfoTag, "#" + msg.obj);
+            sipinfo = "" + msg.obj + sipinfo;
+            //LogUtil.i(SipInfoTag, "Sip信息" + sipinfo);
+
+            if (msg.what == 22) {//释放资源
+                //EventBus.getDefault().post(new MessageEvent(msg.what, EVENT_SIP_REGISTER_STATUS));
+            }
+
+            if (sipinfo.contains("200 OK")) { //注册成功
+                sipStatus = REGISTERCOM;
+//                Log.e(TAG, "SIP注册成功");
+                EventBus.getDefault().post(new MessageEvent(REGISTERCOM, EVENT_SIP_REGISTER_STATUS));
+                if (mSipThread != null) {
+                    mSipThread.interrupt();
+                    mSipThread = null;
+                }
+                if (msg.obj.toString().contains("408")) { //超时
+                    Log.e(TAG, "SIP注册超时");
+                    sipStatus = REGISTERFAIL;
+                    EventBus.getDefault().post(new MessageEvent(REGISTERFAIL, EVENT_SIP_REGISTER_STATUS));
+                    sipRegister();
+                }
+            } else {//注册失败
+                Log.e(TAG, "SIP注册失败");
+                sipStatus = REGISTERFAIL;
+                EventBus.getDefault().post(new MessageEvent(REGISTERFAIL, EVENT_SIP_REGISTER_STATUS));
+                if (mSipThread != null) {
+                    mSipThread.interrupt();
+                    mSipThread = null;
+                }
+                sipRegister();
+            }
+            //failUiRefreshSip();
+
+            if (msg.obj.toString().contains("autocall")) {
+                VvsipCall pCall = null;
+                //LogUtil.e(SipInfoTag, "onClick1");
+                for (VvsipCall _pCall : mVvsipCalls) {
+                    if (_pCall.cid > 0)
+                        //LogUtil.e(SipInfoTag, "state#" + _pCall.mState);
+                        if (_pCall.cid > 0 && _pCall.mState <= 2) {
+                            pCall = _pCall;
+                            break;
+                        }
+                }
+                //LogUtil.e(SipInfoTag, "onClick2");
+                if (pCall == null)
+                    return;
+                //LogUtil.e(SipInfoTag, "onClick3#" + pCall.mState);
+                IVvsipService _service = VvsipService.getService();
+                if (_service == null)
+                    return;
+                VvsipTask _vvsipTask = _service.getVvsipTask();
+                if (_vvsipTask == null)
+                    return;
+                pCall.stop();
+                _service.setSpeakerModeOff();
+            }
+        }
+    };
+
+    /**
+     * ====================Sip注册======================
+     */
+    private Thread mSipThread;
+
+    private static class SipThread extends Thread {
+        WeakReference<Activity> mThreadCallingBedActivity;
+
+        private SipThread(Activity activity) {
+            mThreadCallingBedActivity = new WeakReference<Activity>(activity);
+        }
+
+        @Override
+        public void run() {
+            super.run();
+            if (mThreadCallingBedActivity == null)
+                return;
+            if (mThreadCallingBedActivity.get() != null) {
+                IVvsipService sipService = VvsipService.getService();
+                try {
+                    if (sipService != null && !SipHelper.getInstance().sipinfo.contains("200 OK") /*&& NetHelper.NetConn*/) {
+                        sipStatus = REGISTERING;
+                        EventBus.getDefault().post(new MessageEvent(REGISTERING, EVENT_SIP_REGISTER_STATUS));
+                        sipService.register(sipIP + SIP_IP_END, sipID, sipPWD);
+                        handleCount++;
+                        Log.e(SipInfoTag, "以太网连接,SIP UI状态刷新为注册中");
+                    } else if (sipService != null && SipHelper.getInstance().sipinfo.contains("200 OK")) {
+                        sipStatus = REGISTERCOM;
+                        EventBus.getDefault().post(new MessageEvent(REGISTERCOM, EVENT_SIP_REGISTER_STATUS));
+                    }
+                } catch (NullPointerException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private void sipRegister() {
+        synchronized (this) {
+            mSipThread = new SipThread((Activity) mContext);
+            if (handleCount < 3) {
+                if (mCountDownAutoTimer == null) {
+                    mCountDownAutoTimer = new CountDownTimer(10000, 1000) {
+                        @Override
+                        public void onTick(long l) {
+                        }
+
+                        @Override
+                        public void onFinish() {
+                            handleCount = 0;
+                            if (mSipThread != null) {
+                                mSipThread.start();
+                            }
+                            if (mCountDownAutoTimer != null) {
+                                mCountDownAutoTimer.cancel();
+                                mCountDownAutoTimer = null;
+                            }
+                        }
+                    };
+                    mCountDownAutoTimer.start();
+                }
+                return;
+            } else {
+                if (mCountDownAutoTimer != null) {
+                    mCountDownAutoTimer.cancel();
+                    mCountDownAutoTimer = null;
+                }
+            }
+            if (handleCount == 0) {
+                mSipThread.start();
+            }
+        }
+    }
+
+
+    public void setmSipThread(Thread mSipThread) {
+        this.mSipThread = mSipThread;
+    }
+
+    public Thread getmSipThread() {
+        return mSipThread;
+    }
+
+    /**
+     * 开始通话
+     */
+    public void startCall(String sipUserName) {
+        IVvsipService sipService = VvsipService.getService();
+        if (sipService == null) return;
+        //----------------------------------------------携带呼叫列表转接床头机的Mac地址--------------------------------------------------//
+        sipService.initiateOutgoingCall(sipUserName, "");
+    }
+
+    /**
+     * 结束通话
+     */
+    public void endCall() {
+        VvsipCall call = null;
+        for (VvsipCall pCall : mVvsipCalls) {
+            if (pCall.cid > 0 && pCall.mState <= 2) {
+                call = pCall;
+                break;
+            }
+        }
+        Log.e("xxx","call"+call);
+        if (call == null) return;
+        IVvsipService sipService = VvsipService.getService();
+        Log.e("xxx","sipService"+sipService);
+        if (sipService == null) return;
+        VvsipTask sipTask = sipService.getVvsipTask();
+        Log.e("xxx","sipTask"+sipTask);
+        if (sipTask == null) return;
+        VvsipService.getService().mainEndCall(EXOSIP_CALL_CLOSED);
+        call.stop();
+        sipService.setSpeakerModeOff();
+        sipService.stopPlayer();
+        sipService.setAudioNormalMode();
+    }
+
+    /**
+     * 添加一个电话呼叫对象
+     *
+     * @param call
+     */
+    public void addCallObject(final VvsipCall call) {
+        try {
+            if (call == null) {
+                return;
+            }
+
+            if (mVvsipCalls == null)
+                return;
+            mVvsipCalls.add(call);
+        } catch (Exception e) {
+            //LogUtil.e(SipInfoTag, "onNewVvsipCallEvent: " + e);
+        }
+    }
+
+    /**
+     * 移除一个电话呼叫对象
+     *
+     * @param call
+     */
+    public void removeCallObject(final VvsipCall call) {
+        try {
+            if (call == null) {
+                return;
+            }
+
+            // 4 crash detected here for 4.0.9 with mVvsipCalls=NULL
+            if (mVvsipCalls == null)
+                return;
+            mVvsipCalls.remove(call);
+        } catch (Exception e) {
+            //Log.e(SipInfoTag, "onRemoveVvsipCallEvent: " + e);
+        }
+    }
+
+    /**
+     * 自动接电话
+     */
+    public void autoTalking() {
+        if (mVvsipCalls == null) {
+            mVvsipCalls = new ArrayList<VvsipCall>();
+        }
+        for (VvsipCall _pCall : mVvsipCalls) {
+            if (_pCall.cid > 0 && _pCall.mState < 2 && _pCall.mIncomingCall) {
+                // ANSWER EXISTING CALL
+                int i = _pCall.answer(200, 1);
+              //LogUtil.d(SipInfoTag, "ANSWER EXISTING CALL");
+                IVvsipService _service = VvsipService.getService();
+                if (_service != null) {
+                    if (i >= 0) {
+                        _service.stopPlayer();
+                        _service.setSpeakerModeOff();
+                        _service.setAudioInCallMode();
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+    public static boolean isServiceRunning(Context context, String className) {
+        boolean isRunning = false;
+        ActivityManager activityManager = (ActivityManager) context
+                .getSystemService(Context.ACTIVITY_SERVICE);
+        List<ActivityManager.RunningServiceInfo> serviceList = activityManager
+                .getRunningServices(30);
+
+        if (!(serviceList.size() > 0)) {
+            return false;
+        }
+
+        for (int i = 0; i < serviceList.size(); i++) {
+            if (serviceList.get(i).service.getClassName().equals(className) == true) {
+                isRunning = true;
+                break;
+            }
+        }
+        return isRunning;
+    }
+
+    /**
+     * #############################
+     * <p>
+     * Sip启动服务.
+     * <p>
+     * #############################
+     */
+    private static Handler sipServiceStartHandler = null;
+    private static Runnable sipServiceStartRunnable = null;
+    private static ServiceConnection sipServiceConnection;
+
+    public static Runnable getSipServiceStartRunnable() {
+        return sipServiceStartRunnable;
+    }
+
+    public static Handler getSipServiceStartHandler() {
+        return sipServiceStartHandler;
+    }
+
+    public static ServiceConnection getSipServiceConnection() {
+        return sipServiceConnection;
+    }
+
+    public static void setSipServiceConnection(ServiceConnection sipServiceConnections) {
+        sipServiceConnection = sipServiceConnections;
+    }
+
+    /**
+     * 启动服务
+     */
+    public static Boolean isRegister = false;//是否注册
+
+    public void sipStartService() {
+        sipServiceStartHandler = new Handler();
+
+        sipServiceStartRunnable = new Runnable() {
+            public void run() {
+                if (mContext == null) {
+                    sipServiceStartHandler.postDelayed(sipServiceStartRunnable, 1000);
+                    return;
+                }
+                Intent intent = new Intent(mContext.getApplicationContext(), VvsipService.class);
+                mContext.startService(intent);
+
+                sipServiceConnection = new ServiceConnection() {
+                    public void onServiceConnected(ComponentName name, IBinder service) {
+                        IVvsipService sipservice = ((VvsipServiceBinder) service).getService();
+                        if (sipservice != null) {
+                            //LogUtil.i(SipInfoTag, "Connected!");
+                            if (sipListner != null) {
+                                sipservice.clearListener();
+                                sipservice.addListener(sipListner);
+                            }
+
+                            obtainSipInfo();
+                        }
+                    }
+
+                    public void onServiceDisconnected(ComponentName name) {
+                        //LogUtil.i(SipInfoTag, "Disconnected!");
+                    }
+                };
+
+                isRegister = mContext.bindService(intent, sipServiceConnection, Context.BIND_AUTO_CREATE);
+            }
+        };
+
+        sipServiceStartHandler.postDelayed(sipServiceStartRunnable, 0);
+    }
+
+    //======================Sip回调接口=====================
+    private SipCallBack mSipCallBackI;
+
+    public void setSipCallBack(SipCallBack mSipCallBackI) {
+        this.mSipCallBackI = mSipCallBackI;
+    }
+
+    public SipCallBack getmSipCallBack() {
+        return mSipCallBackI;
+    }
+
+    /**
+     * 用来解析错误代码
+     */
+    public static String analyseErrorCode(String errorCode) {
+        switch (errorCode) {
+            case "200":
+                return "成功";
+            case "408":
+                return "请求超时";
+            case "400":
+                return "服务器不支持请求的语法";
+            case "401":
+                return "未授权";
+            case "403":
+                return "服务器禁止请求";
+            case "404":
+                return "服务器找不到";
+            default:
+                return "未知错误";
+        }
+    }
+
+}

+ 683 - 0
home/src/main/code/com/wdkl/ncs/android/component/home/SipUtil/SipHelperUtil.java

@@ -0,0 +1,683 @@
+package com.wdkl.ncs.android.component.home.SipUtil;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Build;
+import android.os.CountDownTimer;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.vvsip.ansip.IVvsipService;
+import com.vvsip.ansip.IVvsipServiceListener;
+import com.vvsip.ansip.VvsipCall;
+import com.vvsip.ansip.VvsipService;
+import com.vvsip.ansip.VvsipServiceBinder;
+import com.vvsip.ansip.VvsipTask;
+import com.wdkl.ncs.android.component.home.activity.WatchHomeActivity;
+import com.wdkl.ncs.android.component.home.util.EthernetWifiCallBack;
+import com.wdkl.ncs.android.middleware.utils.MessageEvent;
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by dengzhe on 2017/12/18.
+ */
+public class SipHelperUtil implements EthernetWifiCallBack {
+
+    private String TAG = SipHelperUtil.class.getSimpleName();
+
+    //挂断状态  21
+    public static int CALLING_ENDING = 21;
+
+    /**
+     * 注册中
+     */
+    public static final String REGISTERING = "register_ing";
+    /**
+     * 注册失败.
+     */
+    public static final String REGISTERFAIL = "register_fail";
+    /**
+     * 注册完成
+     */
+    public static final String REGISTERCOM = "register_com";
+
+    /**
+     * Sip注册状态
+     */
+    public static final int EVENT_SIP_REGISTER_STATUS = 0x08;
+
+
+    private static String SIP_IP = "192.168.101.1";
+    private static String SIP_IP_PORT = ":5060";
+    private static String SIP_PASS_WORD = "";
+    private static String SIP_ID = "";
+
+    /**
+     * Sip启动注册.
+     */
+    protected int mSipRegisterTime = 3000;
+    private Handler sipRegisterHandler = null;
+    private Runnable sipRegisterRunnable = null;
+
+    /**
+     * SIP信息
+     */
+    public static final String SipInfoTag = "SipInfo";
+    /**
+     * 电话呼叫对象
+     */
+    private List<VvsipCall> mVvsipCalls = null;
+
+
+    private static SipHelperUtil mSipRegisterUtil;
+
+    public Handler getSipRegisterHandler() {
+        return sipRegisterHandler;
+    }
+
+    public Runnable getSipRegisterRunnable() {
+        return sipRegisterRunnable;
+    }
+
+    public List<VvsipCall> getmVvsipCalls() {
+        return mVvsipCalls;
+    }
+
+    private static Context contexts;
+
+    public static SipHelperUtil getInstance(Context context) {
+
+        if (mSipRegisterUtil == null) {
+            synchronized (SipHelperUtil.class) {
+                if (mSipRegisterUtil == null) {
+                    mSipRegisterUtil = new SipHelperUtil();
+                    contexts = context;
+                }
+            }
+        }
+        return mSipRegisterUtil;
+    }
+
+    /**
+     * Instantiates a new Sip register util.
+     */
+    private SipHelperUtil() {
+        setEthernetWifiCallBack(this);
+        if (mVvsipCalls == null) {
+            mVvsipCalls = new ArrayList<VvsipCall>();
+        }
+
+        // Runnable exiting the splash screen and launching the menu
+        sipRegisterRunnable = new Runnable() {
+            public void run() {
+                isSuccessRegisterSip();
+            }
+        };
+
+        // Run the exitRunnable in in mSipRegisterTime ms
+        sipRegisterHandler = new Handler();
+
+        IVvsipService sipservice = VvsipService.getService();
+        if (sipservice != null) {
+            sipRegisterHandler.postDelayed(sipRegisterRunnable, 0);
+            return;
+        }
+        sipRegisterHandler.postDelayed(sipRegisterRunnable, mSipRegisterTime);
+
+
+    }
+
+    /**
+     * 检测Sip服务是否注册成功
+     */
+    public void isSuccessRegisterSip() {
+        VvsipTask vvsipTask = VvsipTask.getVvsipTask();
+        if (vvsipTask != null && VvsipTask.global_failure != 0) {
+            /**
+             * ==================================sip服务启动失败 ================================
+             */
+            Intent intent = new Intent(Intent.ACTION_MAIN);
+            intent.setClass(contexts.getApplicationContext(), VvsipService.class);
+            contexts.stopService(intent);
+//            LogUtil.i(SipInfoTag, "注册失败:lifecycle // isSuccessStartSipService");
+        } else {
+//            finish();
+            /**
+             * ==================================sip服务启动成功 ================================
+             */
+//            LogUtil.i(SipInfoTag, "sip服务启动:lifecycle // isSuccessStartSipService");
+
+        }
+    }
+
+    /**
+     * 注销Sip服务
+     */
+    public void unRegisterSip() {
+
+        IVvsipService sipservice = VvsipService.getService();
+        if (contexts instanceof IVvsipServiceListener && sipservice != null) {
+            sipservice.removeListener((IVvsipServiceListener) contexts);
+        }
+        getSipServiceStartHandler().removeCallbacks(getSipServiceStartRunnable());
+        sipRegisterHandler.removeCallbacks(sipRegisterRunnable);
+        if (getSipServiceConnection() != null && isRegister) {
+            try {
+                contexts.unbindService(getSipServiceConnection());
+                setSipServiceConnection(null);
+            } catch (IllegalArgumentException e) {
+                e.printStackTrace();
+            }
+        }
+        if (mVvsipCalls != null) {
+            mVvsipCalls.clear();
+            mVvsipCalls = null;
+        }
+
+    }
+
+    private boolean isFirstRegister = true;
+    public static String sipMessageCounts = "";
+
+
+
+//    public void setSipListner(IVvsipServiceListener listner) {
+//        sipListner = listner;
+//        IVvsipService sipService = VvsipService.getService();
+//        if (sipService != null && sipListner != null) {
+//            sipService.clearListener();
+//            sipService.addListener(sipListner);
+//        }
+//    }
+
+
+
+
+    /**
+     * Sip信息获取
+     */
+    public void obtainSipInfo() {
+        if (sipMessageCounts.equals(REGISTERCOM) /*&& ethernetS*/) {//sip注册成功并且以太网连上
+            return;
+        }
+        IVvsipService sipService = VvsipService.getService();
+        if (sipService != null) {
+            sipService.addListener((IVvsipServiceListener) contexts);
+            sipService.setMessageHandler(messageHandler);
+        } else {
+//            LogUtil.i(SipInfoTag, "lifecycle // _service==null");
+        }
+        sipRegister();
+//        failUiRefreshSip();
+    }
+
+    /**
+     * Sip信息
+     */
+    private String sipinfo = "";
+    private static int handleCount = 0;
+    //Sip註冊次數
+    private CountDownTimer mCountDownAutoTimer;
+    @SuppressLint("HandlerLeak")
+    private Handler messageHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+//            LogUtil.i(SipInfoTag, "VvsipEvent received (?" + msg.what + " " + msg.arg1
+//                    + " " + msg.arg2 + ")\n");
+//            LogUtil.i(SipInfoTag, "#" + msg.obj);
+            sipinfo = "" + msg.obj + sipinfo;
+//            LogUtil.i(SipInfoTag, "Sip信息" + sipinfo);
+
+            if (msg.what == 22) {//释放资源
+//                EventBus.getDefault().post(new MessageEvent(msg.what, EVENT_SIP_REGISTER_STATUS));
+            }
+
+            if (sipinfo.contains("200 OK")) {//注册成功
+                Log.e(TAG, "SIP注册成功");
+                sipMessageCounts = REGISTERCOM;
+                EventBus.getDefault().post(new MessageEvent(REGISTERCOM, EVENT_SIP_REGISTER_STATUS));
+                if (mSipThread != null) {
+                    mSipThread.interrupt();
+                    mSipThread = null;
+                }
+                if (msg.obj.toString().contains("408")) {//超时
+                    Log.e(TAG, "SIP注册超时");
+                    sipMessageCounts = REGISTERFAIL;
+                    EventBus.getDefault().post(new MessageEvent(REGISTERFAIL, EVENT_SIP_REGISTER_STATUS));
+                    sipRegister();
+                }
+            } else { //注册失败
+
+                Log.e(TAG, "SIP注册失败");
+                sipMessageCounts = REGISTERFAIL;
+                EventBus.getDefault().post(new MessageEvent(REGISTERFAIL, EVENT_SIP_REGISTER_STATUS));
+                if (mSipThread != null) {
+                    mSipThread.interrupt();
+                    mSipThread = null;
+                }
+                sipRegister();
+            }
+//            failUiRefreshSip();
+
+            if (msg.obj.toString().contains("autocall")) {
+                VvsipCall pCall = null;
+//                LogUtil.e(SipInfoTag, "onClick1");
+                for (VvsipCall _pCall : mVvsipCalls) {
+                    if (_pCall.cid > 0)
+//                        LogUtil.e(SipInfoTag, "state#" + _pCall.mState);
+                        if (_pCall.cid > 0 && _pCall.mState <= 2) {
+                            pCall = _pCall;
+                            break;
+                        }
+                }
+//                LogUtil.e(SipInfoTag, "onClick2");
+                if (pCall == null)
+                    return;
+//                LogUtil.e(SipInfoTag, "onClick3#" + pCall.mState);
+                IVvsipService _service = VvsipService.getService();
+                if (_service == null)
+                    return;
+                VvsipTask _vvsipTask = _service.getVvsipTask();
+                if (_vvsipTask == null)
+                    return;
+                pCall.stop();
+                _service.setSpeakerModeOff();
+            }
+        }
+    };
+
+    /**
+     * UI刷新:SIP失败
+     */
+    private void failUiRefreshSip() {
+//        if (!ethernetS) {
+            sipMessageCounts = REGISTERFAIL;
+            EventBus.getDefault().post(new MessageEvent(REGISTERFAIL, EVENT_SIP_REGISTER_STATUS));
+            if (mSipThread != null) {
+                mSipThread.interrupt();
+                mSipThread = null;
+            }
+//            LogUtil.e(SipInfoTag, "以太网断开,SIP UI状态刷新为失败");
+//        }
+    }
+
+    /**
+     * ====================Sip注册======================
+     */
+    private Thread mSipThread;
+
+    private static class SipThread extends Thread {
+        WeakReference<WatchHomeActivity> mThreadCallingDoorActivity;
+
+        public SipThread(WatchHomeActivity activity) {
+            mThreadCallingDoorActivity = new WeakReference<WatchHomeActivity>(
+                    activity);
+        }
+
+        @Override
+        public void run() {
+            super.run();
+            if (mThreadCallingDoorActivity == null)
+                return;
+            if (mThreadCallingDoorActivity.get() != null) {
+                VvsipService sipService = VvsipService.getService();
+                try {
+                    if (sipService != null && !SipHelperUtil.getInstance(contexts).sipinfo.contains("200 OK") /*&& ethernetS*/) {//注册正在进行中
+                        sipMessageCounts = REGISTERING;
+                        EventBus.getDefault().post(new MessageEvent(REGISTERING, EVENT_SIP_REGISTER_STATUS));
+                        sipService.register(SIP_IP + SIP_IP_PORT,
+                                SIP_ID, SIP_PASS_WORD);
+                        //                        LogUtil.i(SipInfoTag, "Sip地址" + Constants.SIP_IP + SIP_IP_END + "\nSip账号" + Constants.SIP_ID + "\nSip密码" + Constants.SIP_PASS_WORD);
+                        handleCount++;
+                        //                        LogUtil.d(SipInfoTag, "Sip第-----" + (handleCount + 1) + "-------次註冊");
+                        Log.e(SipInfoTag, "以太网连接,SIP UI状态刷新为注册中");
+                    } else if (sipService != null && SipHelperUtil.getInstance(contexts).sipinfo.contains("200 OK")) {
+                        sipMessageCounts = REGISTERCOM;
+                        EventBus.getDefault().post(new MessageEvent(REGISTERCOM, EVENT_SIP_REGISTER_STATUS));
+                        Log.e(SipInfoTag, "以太网连接,SIP UI状态刷新为注册完成");
+                    }
+                } catch (NullPointerException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+
+    private void sipRegister() {
+        synchronized (this) {
+            mSipThread = new SipThread((WatchHomeActivity) contexts);
+            if (handleCount < 3) {
+                if (mCountDownAutoTimer == null) {
+                    mCountDownAutoTimer = new CountDownTimer(10000, 1000) {
+                        @Override
+                        public void onTick(long l) {
+//                            LogUtil.d(SipInfoTag, "已經註冊第" + (handleCount + 1) + "次:" + l / 1000 + "秒后重啟註冊");
+                        }
+
+                        @Override
+                        public void onFinish() {
+                            handleCount = 0;
+                            if (mSipThread != null) {
+                                mSipThread.start();
+                            }
+                            if (mCountDownAutoTimer != null) {
+                                mCountDownAutoTimer.cancel();
+                                mCountDownAutoTimer = null;
+                            }
+                        }
+
+                    };
+                    mCountDownAutoTimer.start();
+                }
+                return;
+            } else {
+                if (mCountDownAutoTimer != null) {
+                    mCountDownAutoTimer.cancel();
+                    mCountDownAutoTimer = null;
+                }
+            }
+            if (handleCount == 0) {
+                mSipThread.start();
+            }
+        }
+    }
+
+    public void setmSipThread(Thread mSipThread) {
+        this.mSipThread = mSipThread;
+    }
+
+    public Thread getmSipThread() {
+        return mSipThread;
+    }
+
+    /**
+     * 开始通话
+     */
+    public void startCall(String sipUseName) {
+        IVvsipService sipService = VvsipService.getService();
+        if (sipService == null) return;
+        //----------------------------------------------携带Mac地址(暂时无用)--------------------------------------------------//
+        sipService.initiateOutgoingCall(sipUseName, "");
+    }
+
+    /**
+     * 结束通话
+     */
+    public void endCall() {
+        VvsipCall call = null;
+        for (VvsipCall pCall : mVvsipCalls) {
+            if (pCall.cid > 0 && pCall.mState <= 2) {
+                call = pCall;
+                break;
+            }
+        }
+        Log.e("xxx","call"+call);
+        if (call == null) return;
+        IVvsipService sipService = VvsipService.getService();
+        Log.e("xxx","sipService"+sipService);
+        if (sipService == null) return;
+        VvsipTask sipTask = sipService.getVvsipTask();
+        Log.e("xxx","sipTask"+sipTask);
+        if (sipTask == null) return;
+        VvsipService.getService().mainEndCall(CALLING_ENDING);
+        call.stop();
+        sipService.setSpeakerModeOff();
+        sipService.stopPlayer();
+        sipService.setAudioNormalMode();
+
+    }
+
+    /**
+     * 添加一个电话呼叫对象
+     *
+     * @param call
+     */
+    public void addCallObject(final VvsipCall call) {
+        ((Activity) contexts).runOnUiThread(new Runnable() {
+            public void run() {
+                try {
+                    if (call == null) {
+                        return;
+                    }
+
+                    if (mVvsipCalls == null)
+                        return;
+                    mVvsipCalls.add(call);
+
+                    if (Build.VERSION.SDK_INT >= 5) {
+                        ((Activity) contexts).getWindow().addFlags( // WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+                                // |
+                                WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                                        | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+                                        | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+                    }
+
+                } catch (Exception e) {
+//                    LogUtil.e(SipInfoTag, "onNewVvsipCallEvent: " + e);
+                }
+            }
+        });
+    }
+
+    /**
+     * 移除一个电话呼叫对象
+     *
+     * @param call
+     */
+    public void removeCallObject(final VvsipCall call) {
+        ((Activity) contexts).runOnUiThread(new Runnable() {
+            public void run() {
+                try {
+                    if (call == null) {
+                        return;
+                    }
+
+                    // 4 crash detected here for 4.0.9 with mVvsipCalls=NULL
+                    if (mVvsipCalls == null)
+                        return;
+                    mVvsipCalls.remove(call);
+
+                    if (mVvsipCalls.size() == 0) {
+                        if (Build.VERSION.SDK_INT >= 5) {
+                            ((Activity) contexts).getWindow()
+                                    .clearFlags( // WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+                                            // |
+                                            WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                                                    | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+                                                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+                        }
+                    }
+                } catch (Exception e) {
+//                    Log.e(SipInfoTag, "onRemoveVvsipCallEvent: " + e);
+                }
+            }
+        });
+    }
+
+    /**
+     * 自动接电话
+     */
+    public void autoTalking() {
+        if (mVvsipCalls == null) {
+            mVvsipCalls = new ArrayList<VvsipCall>();
+        }
+        for (VvsipCall _pCall : mVvsipCalls) {
+            if (_pCall.cid > 0 && _pCall.mState < 2 && _pCall.mIncomingCall) {
+                // ANSWER EXISTING CALL
+                int i = _pCall.answer(200, 1);
+//                LogUtil.d(SipInfoTag, "ANSWER EXISTING CALL");
+                IVvsipService _service = VvsipService.getService();
+                if (_service != null) {
+                    if (i >= 0) {
+                        _service.stopPlayer();
+                        _service.setSpeakerModeOff();
+                        _service.setAudioInCallMode();
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+    public static boolean isServiceRunning(Context mContext, String className) {
+
+        boolean isRunning = false;
+        ActivityManager activityManager = (ActivityManager) mContext
+                .getSystemService(Context.ACTIVITY_SERVICE);
+        List<ActivityManager.RunningServiceInfo> serviceList = activityManager
+                .getRunningServices(30);
+
+        if (!(serviceList.size() > 0)) {
+            return false;
+        }
+
+        for (int i = 0; i < serviceList.size(); i++) {
+            if (serviceList.get(i).service.getClassName().equals(className) == true) {
+                isRunning = true;
+                break;
+            }
+        }
+        return isRunning;
+    }
+
+    /**
+     * #############################
+     * <p>
+     * Sip启动服务.
+     * <p>
+     * #############################
+     */
+    private static Handler sipServiceStartHandler = null;
+    private static Runnable sipServiceStartRunnable = null;
+    private static ServiceConnection sipServiceConnection;
+
+    public static Runnable getSipServiceStartRunnable() {
+        return sipServiceStartRunnable;
+    }
+
+    public static Handler getSipServiceStartHandler() {
+        return sipServiceStartHandler;
+    }
+
+    public static ServiceConnection getSipServiceConnection() {
+        return sipServiceConnection;
+    }
+
+    public static void setSipServiceConnection(ServiceConnection sipServiceConnections) {
+        sipServiceConnection = sipServiceConnections;
+    }
+
+    /**
+     * 启动服务
+     */
+    public static Boolean isRegister = false;//是否注册
+
+    public void sipStartService(String sip_ip, String sip_id, String sip_password) {
+        SIP_IP = sip_ip;
+        SIP_ID = sip_id;
+        SIP_PASS_WORD = sip_password;
+
+        sipServiceStartHandler = new Handler();
+
+        sipServiceStartRunnable = new Runnable() {
+            public void run() {
+
+                Intent intent = new Intent(contexts.getApplicationContext(), VvsipService.class);
+                contexts.startService(intent);
+
+                sipServiceConnection = new ServiceConnection() {
+                    public void onServiceConnected(ComponentName name, IBinder service) {
+                        IVvsipService sipservice = ((VvsipServiceBinder) service).getService();
+                        if (contexts instanceof IVvsipServiceListener) {
+                            sipservice.addListener((IVvsipServiceListener) contexts);
+                            Log.e(TAG, "Sip信息获取");
+                            SipHelperUtil.getInstance(contexts).obtainSipInfo();//Sip信息获取
+                        }
+                    }
+
+                    public void onServiceDisconnected(ComponentName name) {
+//                        LogUtil.i(SipInfoTag, "Disconnected!");
+                    }
+                };
+                if (!SipHelperUtil.isServiceRunning(contexts, "com.vvsip.ansip.VvsipService")) {
+                    isRegister = contexts.bindService(intent, sipServiceConnection, Context.BIND_AUTO_CREATE);
+                }
+            }
+        };
+
+        sipServiceStartHandler.postDelayed(sipServiceStartRunnable, 0);
+    }
+
+    /**
+     * 用来解析错误代码
+     */
+    public static String analyseErrorCode(String errorCode) {
+        switch (errorCode) {
+            case "200":
+                return "成功";
+            case "408":
+                return "请求超时";
+            case "400":
+                return "服务器不支持请求的语法";
+            case "401":
+                return "未授权";
+            case "403":
+                return "服务器禁止请求";
+            case "404":
+                return "服务器找不到";
+            default:
+                return "未知错误";
+        }
+    }
+
+    //======================Sip回调接口=====================
+    private SipCallBack mSipCallBack;
+
+    public void setSipCallBack(SipCallBack mSipCallBack) {
+        this.mSipCallBack = mSipCallBack;
+    }
+
+    public SipCallBack getmSipCallBack() {
+        return mSipCallBack;
+    }
+
+    //======================以太网和wifi状态接口=====================
+    private EthernetWifiCallBack mEthernetWifiCallBack;
+
+    public void setEthernetWifiCallBack(EthernetWifiCallBack mEthernetWifiCallBack) {
+        this.mEthernetWifiCallBack = mEthernetWifiCallBack;
+    }
+
+    public EthernetWifiCallBack getmEthernetWifiCallBack() {
+        return mEthernetWifiCallBack;
+    }
+
+    private static boolean ethernetS = true;//获取以太网状态
+    private static boolean wifiS = false;//获取wifi状态
+
+    @Override
+    public boolean ethernetStatus(boolean status) {
+        ethernetS = status;
+        return status;
+    }
+
+    @Override
+    public boolean wifiStatus(boolean status) {
+        wifiS = status;
+        return status;
+    }
+}

+ 701 - 0
home/src/main/code/com/wdkl/ncs/android/component/home/SipUtil/SipHelperUtil2.java

@@ -0,0 +1,701 @@
+package com.wdkl.ncs.android.component.home.SipUtil;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Build;
+import android.os.CountDownTimer;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.util.Log;
+import android.view.WindowManager;
+
+import com.vvsip.ansip.IVvsipService;
+import com.vvsip.ansip.IVvsipServiceListener;
+import com.vvsip.ansip.VvsipCall;
+import com.vvsip.ansip.VvsipService;
+import com.vvsip.ansip.VvsipServiceBinder;
+import com.vvsip.ansip.VvsipTask;
+import com.wdkl.ncs.android.component.home.activity.WatchHomeActivity;
+import com.wdkl.ncs.android.component.home.util.EthernetWifiCallBack;
+import com.wdkl.ncs.android.middleware.utils.MessageEvent;
+
+
+import org.greenrobot.eventbus.EventBus;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+
+
+/**
+ * Created by dengzhe on 2017/12/18.
+ */
+public class SipHelperUtil2 implements EthernetWifiCallBack{
+    private String TAG = SipHelperUtil2.class.getSimpleName();
+    /**
+     * Sip启动注册.
+     */
+    protected int mSipRegisterTime = 3000;
+    private Handler sipRegisterHandler = null;
+    private Runnable sipRegisterRunnable = null;
+
+    /**
+     * SIP信息
+     */
+    public static final String SipInfoTag = "SipInfo";
+    /**
+     * 电话呼叫对象
+     */
+    private List<VvsipCall> mVvsipCalls = null;
+
+    /**
+     * 注册中
+     */
+    public static final String REGISTERING = "register_ing";
+    /**
+     * 注册失败.
+     */
+    public static final String REGISTERFAIL = "register_fail";
+    /**
+     * 注册完成
+     */
+    public static final String REGISTERCOM = "register_com";
+
+    /**
+     * Sip注册状态
+     */
+    public static final int EVENT_SIP_INTERNETPING = 0x01;
+
+    //挂断状态  21
+    public static int CALLING_ENDING = 21;
+
+
+    private static String SIP_IP = "192.168.101.1";
+    private static String SIP_IP_PORT = ":5060";
+    private static String SIP_PASS_WORD = "";
+    private static String SIP_ID = "";
+
+    
+    private static SipHelperUtil2 mSipRegisterUtil;
+    public static String sipMessageCounts = "";
+
+    public Handler getSipRegisterHandler() {
+        return sipRegisterHandler;
+    }
+
+    public Runnable getSipRegisterRunnable() {
+        return sipRegisterRunnable;
+    }
+
+    public List<VvsipCall> getmVvsipCalls() {
+        return mVvsipCalls;
+    }
+
+    /**
+     * Gets instance.
+     *
+     * @param context the context
+     * @return the instance
+     */
+    private static Context contexts;
+
+    public static SipHelperUtil2 getInstance(Context context) {
+
+        if (mSipRegisterUtil == null) {
+            synchronized (SipHelperUtil2.class) {
+                if (mSipRegisterUtil == null) {
+                    mSipRegisterUtil = new SipHelperUtil2();
+                    contexts = context;
+                }
+            }
+        }
+        return mSipRegisterUtil;
+    }
+
+    /**
+     * Instantiates a new Sip register util.
+     */
+    private SipHelperUtil2() {
+        setEthernetWifiCallBack(this);
+        if (mVvsipCalls == null) {
+            mVvsipCalls = new ArrayList<VvsipCall>();
+        }
+
+        // Runnable exiting the splash screen and launching the menu
+        sipRegisterRunnable = new Runnable() {
+            public void run() {
+                isSuccessRegisterSip();
+            }
+        };
+
+        // Run the exitRunnable in in mSipRegisterTime ms
+        sipRegisterHandler = new Handler();
+
+        IVvsipService sipservice = VvsipService.getService();
+        if (sipservice != null) {
+            sipRegisterHandler.postDelayed(sipRegisterRunnable, 0);
+            return;
+        }
+        sipRegisterHandler.postDelayed(sipRegisterRunnable, mSipRegisterTime);
+
+
+    }
+
+    /**
+     * 检测Sip服务是否注册成功
+     */
+    public void isSuccessRegisterSip() {
+        VvsipTask vvsipTask = VvsipTask.getVvsipTask();
+        if (vvsipTask != null && VvsipTask.global_failure != 0) {
+            /**
+             * ==================================sip服务启动失败 ================================
+             */
+            Intent intent = new Intent(Intent.ACTION_MAIN);
+            intent.setClass(contexts.getApplicationContext(), VvsipService.class);
+            contexts.stopService(intent);
+//            LogUtil.i(SipInfoTag, "注册失败:lifecycle // isSuccessStartSipService");
+        } else {
+//            finish();
+            /**
+             * ==================================sip服务启动成功 ================================
+             */
+//            Intent intent = new Intent();
+//            intent.setClass(this, SipSuccessActivity.class);
+//            startActivity(intent);
+//            LogUtil.i(SipInfoTag, "sip服务启动:lifecycle // isSuccessStartSipService");
+
+        }
+    }
+
+    /**
+     * 注销Sip服务
+     */
+    public void unRegisterSip() {
+//        LogUtil.i(SipInfoTag, "lifecycle // onDestroy");
+
+        IVvsipService sipservice = VvsipService.getService();
+        if (contexts instanceof IVvsipServiceListener && sipservice != null) {
+            sipservice.removeListener((IVvsipServiceListener) contexts);
+        }
+        getSipServiceStartHandler().removeCallbacks(getSipServiceStartRunnable());
+        sipRegisterHandler.removeCallbacks(sipRegisterRunnable);
+        if (getSipServiceConnection() != null && isRegister) {
+            try {
+                contexts.unbindService(getSipServiceConnection());
+                setSipServiceConnection(null);
+            } catch (IllegalArgumentException e) {
+                e.printStackTrace();
+            }
+        }
+        if (mVvsipCalls != null) {
+            mVvsipCalls.clear();
+            mVvsipCalls = null;
+        }
+
+//        Log.i(SipInfoTag, "lifecycle // onDestroy");
+    }
+
+    private boolean isFirstRegister = true;
+
+    /**
+     * Sip信息获取
+     */
+    public void obtainSipInfo() {
+        if (sipMessageCounts.equals(REGISTERCOM) /*&& ethernetS*/) {//sip注册成功并且以太网连上
+            return;
+        }
+        IVvsipService sipService = VvsipService.getService();
+        if (sipService != null) {
+            sipService.addListener((IVvsipServiceListener) contexts);
+            sipService.setMessageHandler(messageHandler);
+        } else {
+//            LogUtil.i(SipInfoTag, "lifecycle // _service==null");
+        }
+        sipRegister();
+        failUiRefreshSip();
+    }
+
+    /**
+     * Sip信息
+     */
+    private String sipinfo = "";
+    private static int handleCount = 0;
+    //Sip註冊次數
+    private CountDownTimer mCountDownAutoTimer;
+    @SuppressLint("HandlerLeak")
+    private Handler messageHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+//            LogUtil.i(SipInfoTag, "VvsipEvent received (?" + msg.what + " " + msg.arg1
+//                    + " " + msg.arg2 + ")\n");
+//            LogUtil.i(SipInfoTag, "#" + msg.obj);
+            sipinfo = "" + msg.obj + sipinfo;
+//            LogUtil.i(SipInfoTag, "Sip信息" + sipinfo);
+
+            if (msg.what == 22) {//释放资源
+//                EventBus.getDefault().post(new MessageEvent(msg.what, EVENT_SIP_INTERNETPING));
+            }
+
+            if (sipinfo.contains("200 OK")) {//注册成功
+                Log.e(TAG, "SIP注册成功");
+                sipMessageCounts = REGISTERCOM;
+                EventBus.getDefault().post(new MessageEvent(REGISTERCOM, EVENT_SIP_INTERNETPING));
+                if (mSipThread != null) {
+                    mSipThread.interrupt();
+                    mSipThread = null;
+                }
+                if (msg.obj.toString().contains("408")) {//超时
+                    Log.e(TAG, "SIP注册超时");
+                    sipMessageCounts = REGISTERFAIL;
+                    EventBus.getDefault().post(new MessageEvent(REGISTERFAIL, EVENT_SIP_INTERNETPING));
+                    sipRegister();
+                }
+            } else {//注册失败
+                Log.e(TAG, "SIP注册失败");
+                sipMessageCounts = REGISTERFAIL;
+                EventBus.getDefault().post(new MessageEvent(REGISTERFAIL, EVENT_SIP_INTERNETPING));
+                if (mSipThread != null) {
+                    mSipThread.interrupt();
+                    mSipThread = null;
+                }
+                sipRegister();
+            }
+            failUiRefreshSip();
+
+            if (msg.obj.toString().contains("autocall")) {
+                VvsipCall pCall = null;
+//                LogUtil.e(SipInfoTag, "onClick1");
+                for (VvsipCall _pCall : mVvsipCalls) {
+                    if (_pCall.cid > 0)
+//                        LogUtil.e(SipInfoTag, "state#" + _pCall.mState);
+                        if (_pCall.cid > 0 && _pCall.mState <= 2) {
+                            pCall = _pCall;
+                            break;
+                        }
+                }
+//                LogUtil.e(SipInfoTag, "onClick2");
+                if (pCall == null)
+                    return;
+//                LogUtil.e(SipInfoTag, "onClick3#" + pCall.mState);
+                IVvsipService _service = VvsipService.getService();
+                if (_service == null)
+                    return;
+                VvsipTask _vvsipTask = _service.getVvsipTask();
+                if (_vvsipTask == null)
+                    return;
+                pCall.stop();
+                _service.setSpeakerModeOff();
+            }
+        }
+    };
+
+    /**
+     * ====================Sip注册======================
+     */
+    private Thread mSipThread;
+
+    private static class SipThread extends Thread {
+        WeakReference<WatchHomeActivity> mThreadCallingDoorActivity;
+
+        public SipThread(WatchHomeActivity activity) {
+            mThreadCallingDoorActivity = new WeakReference<WatchHomeActivity>(
+                    activity);
+        }
+
+
+        @Override
+        public void run() {
+            super.run();
+            if (mThreadCallingDoorActivity == null)
+                return;
+            if (mThreadCallingDoorActivity.get() != null) {
+                VvsipService sipService = VvsipService.getService();
+                try {
+                    if (sipService != null && !SipHelperUtil2.getInstance(contexts).sipinfo.contains("200 OK") /*&& ethernetS*/) {//注册正在进行中
+                        sipMessageCounts = REGISTERING;
+                        EventBus.getDefault().post(new MessageEvent(REGISTERING, EVENT_SIP_INTERNETPING));
+                        sipService.register(SIP_IP + SIP_IP_PORT,
+                               SIP_ID, SIP_PASS_WORD);
+//                    LogUtil.i(SipInfoTag, "Sip地址" + Constants.SIP_IP + SIP_IP_END + "\nSip账号" + Constants.SIP_ID + "\nSip密码" + Constants.SIP_PASS_WORD);
+                        handleCount++;
+//                        LogUtil.d(SipInfoTag, "Sip第-----" + (handleCount + 1) + "-------次註冊");
+                        Log.e(SipInfoTag, "以太网连接,SIP UI状态刷新为注册中");
+                    } else if (sipService != null && SipHelperUtil2.getInstance(contexts).sipinfo.contains("200 OK")) {
+                        Log.e(SipInfoTag, "以太网连接,SIP UI状态刷新为注册完成");
+                        sipMessageCounts = REGISTERCOM;
+                        EventBus.getDefault().post(new MessageEvent(REGISTERCOM, EVENT_SIP_INTERNETPING));
+                    }
+                } catch (NullPointerException e) {
+                    e.printStackTrace();
+
+
+                }
+
+            }
+        }
+    }
+
+    private void sipRegister() {
+        synchronized (this) {
+            mSipThread = new SipThread((WatchHomeActivity) contexts);
+            if (handleCount < 3) {
+                if (mCountDownAutoTimer == null) {
+                    mCountDownAutoTimer = new CountDownTimer(10000, 1000) {
+                        @Override
+                        public void onTick(long l) {
+//                            LogUtil.d(SipInfoTag, "已經註冊第" + (handleCount + 1) + "次:" + l / 1000 + "秒后重啟註冊");
+                        }
+
+                        @Override
+                        public void onFinish() {
+                            handleCount = 0;
+                            if (mSipThread != null) {
+                                mSipThread.start();
+                            }
+                            if (mCountDownAutoTimer != null) {
+                                mCountDownAutoTimer.cancel();
+                                mCountDownAutoTimer = null;
+                            }
+                        }
+
+                    };
+                    mCountDownAutoTimer.start();
+                }
+                return;
+            } else {
+                if (mCountDownAutoTimer != null) {
+                    mCountDownAutoTimer.cancel();
+                    mCountDownAutoTimer = null;
+                }
+            }
+            if (handleCount == 0) {
+                mSipThread.start();
+//                LogUtil.d(SipInfoTag, "Sip第一次註冊");
+            }
+        }
+    }
+
+    /**
+     * UI刷新:SIP失败
+     */
+    public void failUiRefreshSip() {
+//        if (!ethernetS) {
+//            sipMessageCounts = REGISTERFAIL;
+//            EventBus.getDefault().post(new MessageEvent(REGISTERFAIL, EVENT_SIP_INTERNETPING));
+//            if (mSipThread != null) {
+//                mSipThread.interrupt();
+//                mSipThread = null;
+//            }
+////            LogUtil.e(SipInfoTag, "以太网断开,SIP UI状态刷新为失败");
+//        }
+    }
+
+    public void setmSipThread(Thread mSipThread) {
+        this.mSipThread = mSipThread;
+    }
+
+    public Thread getmSipThread() {
+        return mSipThread;
+    }
+
+    /**
+     * 开始通话
+     */
+    public void startCall(String sipUseName, String mac) {
+        IVvsipService sipService = VvsipService.getService();
+        if (sipService == null) return;
+        //----------------------------------------------携带拨打对应床头机Mac地址--------------------------------------------------//
+        sipService.initiateOutgoingCall(sipUseName, mac);
+    }
+
+    /**
+     * 结束通话
+     */
+    public void endCall() {
+        VvsipCall call = null;
+        for (VvsipCall pCall : mVvsipCalls) {
+            if (pCall.cid > 0 && pCall.mState <= 2) {
+                call = pCall;
+                break;
+            }
+        }
+        Log.e("xxx","call"+call);
+        if (call == null) return;
+        IVvsipService sipService = VvsipService.getService();
+        Log.e("xxx","sipService"+sipService);
+        if (sipService == null) return;
+        VvsipTask sipTask = sipService.getVvsipTask();
+        Log.e("xxx","sipTask"+sipTask);
+        if (sipTask == null) return;
+        VvsipService.getService().mainEndCall(CALLING_ENDING);
+        call.stop();
+        sipService.setSpeakerModeOff();
+        sipService.stopPlayer();
+        sipService.setAudioNormalMode();
+
+    }
+
+    /**
+     * 添加一个电话呼叫对象
+     *
+     * @param call
+     */
+    public void addCallObject(final VvsipCall call) {
+        ((Activity) contexts).runOnUiThread(new Runnable() {
+            public void run() {
+                try {
+                    if (call == null) {
+                        return;
+                    }
+
+                    if (mVvsipCalls == null)
+                        return;
+                    mVvsipCalls.add(call);
+
+                    if (Build.VERSION.SDK_INT >= 5) {
+                        ((Activity) contexts).getWindow().addFlags( // WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+                                // |
+                                WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                                        | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+                                        | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+                    }
+
+                } catch (Exception e) {
+//                    LogUtil.e(SipInfoTag, "onNewVvsipCallEvent: " + e);
+                }
+            }
+        });
+    }
+
+    /**
+     * 移除一个电话呼叫对象
+     *
+     * @param call
+     */
+    public void removeCallObject(final VvsipCall call) {
+        ((Activity) contexts).runOnUiThread(new Runnable() {
+            public void run() {
+                try {
+                    if (call == null) {
+                        return;
+                    }
+
+                    // 4 crash detected here for 4.0.9 with mVvsipCalls=NULL
+                    if (mVvsipCalls == null)
+                        return;
+                    mVvsipCalls.remove(call);
+
+                    if (mVvsipCalls.size() == 0) {
+                        if (Build.VERSION.SDK_INT >= 5) {
+                            ((Activity) contexts).getWindow()
+                                    .clearFlags( // WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
+                                            // |
+                                            WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                                                    | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+                                                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+                        }
+                    }
+                } catch (Exception e) {
+//                    Log.e(SipInfoTag, "onRemoveVvsipCallEvent: " + e);
+                }
+            }
+        });
+    }
+
+    /**
+     * 自动接电话
+     */
+    public void autoTalking() {
+        if (mVvsipCalls == null) {
+            mVvsipCalls = new ArrayList<VvsipCall>();
+        }
+        for (VvsipCall _pCall : mVvsipCalls) {
+            if (_pCall.cid > 0 && _pCall.mState < 2 && _pCall.mIncomingCall) {
+                // ANSWER EXISTING CALL
+                int i = _pCall.answer(200, 1);
+//                LogUtil.d(SipInfoTag, "ANSWER EXISTING CALL");
+                IVvsipService _service = VvsipService.getService();
+                if (_service != null) {
+                    if (i >= 0) {
+                        _service.stopPlayer();
+                        _service.setSpeakerModeOff();
+                        _service.setAudioInCallMode();
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+
+    /**
+     * 【返回值说明】0:成功,其他:失败
+     *
+     * 取值范围 0.0--1.0
+     */
+//    public int setSipVolume(float capturegain, float playbackgain) {
+//        VvsipService service = VvsipService.getService();
+//        if (null != service) {
+//            return service.setSipVolume(capturegain, playbackgain);
+//        }
+//
+//        return -1;
+//    }
+
+
+    public static boolean isServiceRunning(Context mContext, String className) {
+
+        boolean isRunning = false;
+        ActivityManager activityManager = (ActivityManager) mContext
+                .getSystemService(Context.ACTIVITY_SERVICE);
+        List<ActivityManager.RunningServiceInfo> serviceList = activityManager
+                .getRunningServices(30);
+
+        if (!(serviceList.size() > 0)) {
+            return false;
+        }
+
+        for (int i = 0; i < serviceList.size(); i++) {
+            if (serviceList.get(i).service.getClassName().equals(className) == true) {
+                isRunning = true;
+                break;
+            }
+        }
+        return isRunning;
+    }
+
+    /**
+     * #############################
+     * <p>
+     * Sip启动服务.
+     * <p>
+     * #############################
+     */
+    private static Handler sipServiceStartHandler = null;
+    private static Runnable sipServiceStartRunnable = null;
+    private static ServiceConnection sipServiceConnection;
+
+    public static Runnable getSipServiceStartRunnable() {
+        return sipServiceStartRunnable;
+    }
+
+    public static Handler getSipServiceStartHandler() {
+        return sipServiceStartHandler;
+    }
+
+    public static ServiceConnection getSipServiceConnection() {
+        return sipServiceConnection;
+    }
+
+    public static void setSipServiceConnection(ServiceConnection sipServiceConnections) {
+        sipServiceConnection = sipServiceConnections;
+    }
+
+    /**
+     * 启动服务
+     */
+    public static Boolean isRegister = false;//是否注册
+
+    public void sipStartService(String sip_ip, String sip_id, String sip_password) {
+        SIP_IP = sip_ip;
+        SIP_ID = sip_id;
+        SIP_PASS_WORD = sip_password;
+
+
+        sipServiceStartHandler = new Handler();
+
+        sipServiceStartRunnable = new Runnable() {
+            public void run() {
+
+                Intent intent = new Intent(contexts.getApplicationContext(), VvsipService.class);
+                contexts.startService(intent);
+
+                sipServiceConnection = new ServiceConnection() {
+                    public void onServiceConnected(ComponentName name, IBinder service) {
+                        IVvsipService sipservice = ((VvsipServiceBinder) service).getService();
+                        if (contexts instanceof IVvsipServiceListener) {
+                            sipservice.addListener((IVvsipServiceListener) contexts);
+//                            LogUtil.i(SipInfoTag, "Connected!");
+                            SipHelperUtil2.getInstance(contexts).obtainSipInfo();//Sip信息获取
+                        }
+                    }
+
+                    public void onServiceDisconnected(ComponentName name) {
+//                        LogUtil.i(SipInfoTag, "Disconnected!");
+                    }
+                };
+                if (!SipHelperUtil2.isServiceRunning(contexts, "com.vvsip.ansip.VvsipService")) {
+                    isRegister = contexts.bindService(intent, sipServiceConnection, Context.BIND_AUTO_CREATE);
+                }
+            }
+        };
+
+        sipServiceStartHandler.postDelayed(sipServiceStartRunnable, 0);
+    }
+
+    /**
+     * 用来解析错误代码
+     */
+    public static String analyseErrorCode(String errorCode) {
+        switch (errorCode) {
+            case "200":
+                return "成功";
+            case "408":
+                return "请求超时";
+            case "400":
+                return "服务器不支持请求的语法";
+            case "401":
+                return "未授权";
+            case "403":
+                return "服务器禁止请求";
+            case "404":
+                return "服务器找不到";
+            default:
+                return "未知错误";
+        }
+    }
+
+    //======================Sip回调接口=====================
+    private SipCallBack mSipCallBackI;
+
+    public void setSipCallBack(SipCallBack mSipCallBackI) {
+        this.mSipCallBackI = mSipCallBackI;
+    }
+
+    public SipCallBack getmSipCallBack() {
+        return mSipCallBackI;
+    }
+
+    //======================以太网和wifi状态接口=====================
+    private EthernetWifiCallBack mEthernetWifiCallBackI;
+
+    public void setEthernetWifiCallBack(EthernetWifiCallBack mEthernetWifiCallBackI) {
+        this.mEthernetWifiCallBackI = mEthernetWifiCallBackI;
+    }
+
+    public EthernetWifiCallBack getmEthernetWifiCallBackI() {
+        return mEthernetWifiCallBackI;
+    }
+
+    private static boolean ethernetS = false;//获取以太网状态
+    private static boolean wifiS = false;//获取wifi状态
+
+    @Override
+    public boolean ethernetStatus(boolean status) {
+        ethernetS = status;
+        return status;
+    }
+
+    @Override
+    public boolean wifiStatus(boolean status) {
+        wifiS = status;
+        return status;
+    }
+}

+ 20 - 6
home/src/main/code/com/wdkl/ncs/android/component/home/activity/RTCVoipAudioRingingActivity.java

@@ -47,8 +47,9 @@ public class RTCVoipAudioRingingActivity extends Activity implements View.OnClic
     private TcpModel tcpModel = new TcpModel();
     private TextView bao_mother_name_tv;
 
-    private CountDownTimer countDownTimer;
-    private int countdownTime = 15;
+    private CountDownTimer countDownTimer;//倒计时控制器
+    private int countdownTime = 15;//默认转接时间
+    private boolean isAnswerOrHangUp = false;//加一个变量防止在最后一秒点击了接听或者挂断 还是会转发出去
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -92,10 +93,17 @@ public class RTCVoipAudioRingingActivity extends Activity implements View.OnClic
         MLOC.addHistory(historyBean, true);
 
         initCountDownTimer();
-        countDownTimer.start();
+        //没有护士说明是其他角色 不要转接功能
+        if(!Constants.Companion.getUser_role_name().contains("护士")){
+            findViewById(R.id.change_over_relayout).setVisibility(View.GONE);
+        }else {
+            // 是护士才开启自动转接
+            countDownTimer.start();
+        }
         MediaPlayHelper.getInstance().playResMusic(R.raw.incoming_call, 1.0f, true);
-
         EventBus.getDefault().register(this);
+
+
     }
 
     public void addListener() {
@@ -141,9 +149,12 @@ public class RTCVoipAudioRingingActivity extends Activity implements View.OnClic
     @Override
     protected void onDestroy() {
         super.onDestroy();
+        Log.e(TAG,"onDestroy()");
+        isAnswerOrHangUp = false;
         if (countDownTimer != null){
             countDownTimer.cancel();
         }
+        MediaPlayHelper.getInstance().stopMusic();
         EventBus.getDefault().unregister(this);
     }
 
@@ -151,6 +162,7 @@ public class RTCVoipAudioRingingActivity extends Activity implements View.OnClic
     public void onClick(View v) {
         int id = v.getId();
         if (id == R.id.hang_up_imagev) {
+            isAnswerOrHangUp = true;
             MediaPlayHelper.getInstance().stopMusic();
             countDownTimer.cancel();
             //todo 给服务器发送拒接 tcp
@@ -194,6 +206,8 @@ public class RTCVoipAudioRingingActivity extends Activity implements View.OnClic
             });
 
         } else if (id == R.id.call_the_voice_imagev) {
+            Log.e(TAG,"点击了接听");
+            isAnswerOrHangUp = true;
             MediaPlayHelper.getInstance().stopMusic();
             countDownTimer.cancel();
             //todo 给服务器发送接听 tcp
@@ -215,8 +229,6 @@ public class RTCVoipAudioRingingActivity extends Activity implements View.OnClic
 
     private void initCountDownTimer() {
 
-        Log.e(TAG,"时间到 转发了");
-
         countdownTime = SettingConfig.getCountdownTime(this);
         countDownTimer = new CountDownTimer(countdownTime*1000, 1000) {
             @Override
@@ -226,6 +238,8 @@ public class RTCVoipAudioRingingActivity extends Activity implements View.OnClic
 
             @Override
             public void onFinish() {
+                if(isAnswerOrHangUp)return;
+                Log.e(TAG,"时间到 转发了");
                 MediaPlayHelper.getInstance().stopMusic();
                 //todo 给服务器发送转接 tcp
                 InteractionVO interactionVO = new Gson().fromJson(tcpModel.getData().toString(), InteractionVO.class);

+ 225 - 0
home/src/main/code/com/wdkl/ncs/android/component/home/activity/SipVoipAudioActivity.kt

@@ -0,0 +1,225 @@
+package com.wdkl.ncs.android.component.home.activity
+
+import android.app.Activity
+import android.os.Bundle
+import android.os.SystemClock
+import android.support.v7.app.AlertDialog
+import android.util.Log
+import android.view.View
+import android.view.WindowManager
+import com.google.gson.Gson
+import com.vvsip.ansip.IVvsipServiceListener
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.SipUtil.SipHelper
+import com.wdkl.ncs.android.component.home.SipUtil.SipHelperUtil
+import com.wdkl.ncs.android.component.home.SipUtil.SipHelperUtil2
+import com.wdkl.ncs.android.component.home.util.MediaPlayHelper
+import com.wdkl.ncs.android.component.nursehome.common.Constants
+import com.wdkl.ncs.android.lib.utils.showMessage
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
+import com.wdkl.ncs.android.middleware.tcp.TcpClient
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel
+import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpType
+import com.wdkl.ncs.android.middleware.utils.MessageEvent
+import kotlinx.android.synthetic.main.activity_sip_voip_audio.*
+import kotlinx.android.synthetic.main.activity_sip_voip_audio.bao_mother_name_tv
+import kotlinx.android.synthetic.main.activity_sip_voip_audio.call_duration_tv
+import kotlinx.android.synthetic.main.activity_sip_voip_audio.hang_up_imagev
+import kotlinx.android.synthetic.main.activity_sip_voip_audio.room_number_tv
+import kotlinx.android.synthetic.main.activity_sip_voip_audio_ringing.*
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+class SipVoipAudioActivity: Activity(), View.OnClickListener {
+    var TAG = SipVoipAudioActivity::class.java.simpleName
+
+
+    var ACTION = "ACTION"
+    var RING = "RING"
+    var CALLING = "CALLING"
+    var CALL = "CALL"
+    private var action: String? = null
+    private var targetId: String? = null
+    var tcpModel = TcpModel()
+
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+                WindowManager.LayoutParams.FLAG_FULLSCREEN)
+        setContentView(R.layout.activity_sip_voip_audio)
+
+        //todo 设置为通话状态 有其他用户输入时 DeviceChannel设置返回通话中
+        DeviceChannel.calling = true
+        init()
+
+
+        targetId = intent.getStringExtra("targetId")
+        Log.e("RTCVoipAudioActivity", "SIP账号$targetId")
+        tcpModel = intent.getSerializableExtra("TcpModel") as TcpModel
+//        interactionVO = (InteractionVO) getIntent().getSerializableExtra("interactionVO");
+        action = intent.getStringExtra(ACTION)
+
+        MediaPlayHelper.getInstance().playResMusic(R.raw.outgoing_call, 0.6f, true)
+        if (tcpModel.type == TcpType.VOICE) {
+            if (tcpModel.action === TcpAction.VoiceAction.CALL) {
+                val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
+                room_number_tv.text =  interactionVO.fromFrameFullName
+                bao_mother_name_tv.setText(interactionVO.fromMemberName)
+            } else if (tcpModel.action === TcpAction.VoiceAction.SUCCESS) {
+                val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
+                room_number_tv.text =  interactionVO.toFrameFullName
+                bao_mother_name_tv.setText(interactionVO.toMemberName)
+            }
+        }
+
+        if (action == CALLING) {
+            Log.e(TAG,"自己同意 拨打过去。。。")
+            //主动拨打电话
+            SipHelperUtil.getInstance(this).getmSipCallBack().startCall(targetId)
+            call_duration_tv.visibility = View.VISIBLE
+            call_duration_tv.base = SystemClock.elapsedRealtime()
+            call_duration_tv.start()
+        } else if (action == RING) {
+            Log.e(TAG,"接电话。。。")
+            //接听电话
+            SipHelperUtil.getInstance(this).getmSipCallBack().autoTalking()
+        }
+
+
+    }
+
+    override fun onStart() {
+        super.onStart()
+        if(!EventBus.getDefault().isRegistered(this)) {
+            EventBus.getDefault().register(this)
+        }
+    }
+
+
+
+    fun init(){
+        hang_up_imagev.setOnClickListener(this)
+
+
+
+    }
+    override fun onClick(p0: View?) {
+        when(p0?.id){
+          R.id.hang_up_imagev ->{
+              var interactionVO: InteractionVO? = null
+              if (tcpModel.data.javaClass.name == String::class.java.name) {
+                  interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
+              } else {
+                  interactionVO = tcpModel.data as InteractionVO
+              }
+              Log.e(TAG, "tcpModel" + interactionVO!!.id!!)
+              Log.e(TAG, "tcpModel" + tcpModel.toJson())
+              //todo 给服务器发送挂断 tcp
+              if (interactionVO != null) {
+                  if (tcpModel.type == TcpType.VOICE) {
+                      if (tcpModel.action === TcpAction.VoiceAction.SUCCESS) {
+                          val voiceUtilTcpModel = VoiceUtil.voiceHandoff(Integer.parseInt(Constants.ids), tcpModel.toId, interactionVO.id)
+                          TcpClient.getInstance().sendMsg(voiceUtilTcpModel.toJson())
+                      } else if (tcpModel.action === TcpAction.VoiceAction.CALL) {
+                          val voiceUtilTcpModel = VoiceUtil.voiceHandoff(Integer.parseInt(Constants.ids), tcpModel.fromId, interactionVO.id)
+                          TcpClient.getInstance().sendMsg(voiceUtilTcpModel.toJson())
+                      }
+                      finish()
+                      SipHelperUtil.getInstance(this).getmSipCallBack().endCall()
+
+                  }
+
+              }
+
+          }
+
+
+        }
+
+    }
+
+
+
+
+
+
+
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+   fun onMoonEvent(messageEvent: MessageEvent) {
+        if (messageEvent.tag == 2) {
+            val tcpModel = messageEvent.getMessage() as TcpModel
+
+            if (tcpModel.action === TcpAction.VoiceAction.ACCEPT) {
+                MediaPlayHelper.getInstance().stopMusic()
+                Log.i(TAG, "对方接听电话啦")
+                //data 是 InteractionVO
+                //   Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
+                //  InteractionVO interactionVO = gson.fromJson(tcpModel.getData().toString(), InteractionVO.class);
+                MediaPlayHelper.getInstance().stopMusic()
+                call_duration_tv.visibility = View.VISIBLE
+                call_duration_tv.base = SystemClock.elapsedRealtime()
+                call_duration_tv.start()
+                SipHelperUtil.getInstance(this).getmSipCallBack().startCall(targetId)
+            }else if (tcpModel.action  == TcpAction.VoiceAction.HANDOFF) {//对方挂断
+                call_duration_tv.stop()
+                finish()
+            }else if (tcpModel.action === TcpAction.VoiceAction.REJECT) {
+                //voiceStatus.setText("对方拒绝接听");
+                showMessage("对方拒绝接听")
+                MediaPlayHelper.getInstance().stopMusic()
+                finish()
+            } else if (tcpModel.action === TcpAction.VoiceAction.CALLING) {
+                //voiceStatus.setText("对方正在通话");
+                showMessage("对方正在通话")
+                MediaPlayHelper.getInstance().stopMusic()
+                finish()
+            } else if (tcpModel.action === TcpAction.VoiceAction.FAILED) {
+                //voiceStatus.setText("对方拒绝");
+                showMessage("呼叫失败,对方可能不在线")
+                MediaPlayHelper.getInstance().stopMusic()
+                finish()
+            }
+        }
+    }
+
+
+
+
+
+
+
+
+    override fun onBackPressed(){
+        AlertDialog.Builder(this).setCancelable(true)
+                .setTitle("是否挂断?")
+                .setNegativeButton("取消") { arg0, arg1 -> }.setPositiveButton("确定"
+                ) { arg0, arg1 ->
+                    //                    timer.stop()
+//                    voipManager.hangup(object : IXHResultCallback {
+//                        override fun success(data: Any) {
+////                            stopAndFinish()
+//                        }
+//
+//                        override fun failed(errMsg: String) {
+////                            MLOC.d("", "AEVENT_VOIP_ON_STOP errMsg:$errMsg")
+////                            MLOC.showMsg(this@SipVoipAudioActivity, errMsg)
+//                        }
+//                    })
+                }.show()
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        //todo 状态设置为未在通话中
+        DeviceChannel.calling = false
+        MediaPlayHelper.getInstance().stopMusic()
+        EventBus.getDefault().unregister(this)
+    }
+
+}

+ 180 - 0
home/src/main/code/com/wdkl/ncs/android/component/home/activity/SipVoipAudioRingingActivity.kt

@@ -0,0 +1,180 @@
+package com.wdkl.ncs.android.component.home.activity
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import android.os.CountDownTimer
+import android.util.Log
+import android.view.View
+import android.view.WindowManager
+import com.google.gson.Gson
+import com.wdkl.ncs.android.component.home.R
+import com.wdkl.ncs.android.component.home.settingconfig.SettingConfig
+import com.wdkl.ncs.android.component.home.util.MediaPlayHelper
+import com.wdkl.ncs.android.component.nursehome.common.Constants
+import com.wdkl.ncs.android.middleware.model.vo.InteractionVO
+import com.wdkl.ncs.android.middleware.tcp.TcpClient
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel
+import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil
+import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
+import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction
+import com.wdkl.ncs.android.middleware.utils.MessageEvent
+import kotlinx.android.synthetic.main.activity_sip_voip_audio_ringing.*
+import org.greenrobot.eventbus.EventBus
+import org.greenrobot.eventbus.Subscribe
+import org.greenrobot.eventbus.ThreadMode
+
+class SipVoipAudioRingingActivity : Activity(), View.OnClickListener {
+    var TAG = SipVoipAudioRingingActivity::class.java.simpleName
+
+    private var targetId: String? = null
+    private var interactionVO = InteractionVO()
+    private var tcpModel = TcpModel()
+
+    private var countDownTimer: CountDownTimer? = null//倒计时控制器
+    private var countdownTime = 15//默认转接时间
+    private var isAnswerOrHangUp = false//加一个变量防止在最后一秒点击了接听或者挂断 还是会转发出去
+
+
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+                WindowManager.LayoutParams.FLAG_FULLSCREEN)
+        setContentView(R.layout.activity_sip_voip_audio_ringing)
+        //todo 设置为通话状态 有其他用户输入时 DeviceChannel设置返回通话中
+        DeviceChannel.calling = true
+
+        targetId = intent.getStringExtra("targetId")
+        tcpModel = intent.getSerializableExtra("TcpModel") as TcpModel
+        interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
+        init()
+
+        initCountDownTimer()
+        //没有护士说明是其他角色 不要转接功能
+        if (!Constants.user_role_name!!.contains("护士")) {
+            change_over_relayout.setVisibility(View.GONE)
+        } else {
+            // 是护士才开启自动转接
+            countDownTimer?.start()
+        }
+        MediaPlayHelper.getInstance().playResMusic(R.raw.incoming_call, 1.0f, true)
+    }
+
+    fun init(){
+        room_number_tv.text =  interactionVO.fromFrameFullName
+        bao_mother_name_tv.text = interactionVO.getFromMemberName()
+
+        change_over_relayout.setOnClickListener(this)
+        hang_up_imagev.setOnClickListener(this)
+        call_the_voice_imagev.setOnClickListener(this)
+    }
+
+    override fun onStart() {
+        super.onStart()
+        if(!EventBus.getDefault().isRegistered(this)) {
+            EventBus.getDefault().register(this)
+        }
+    }
+
+
+    override fun onClick(p0: View?) {
+        when(p0?.id){
+            R.id.change_over_relayout ->{
+                MediaPlayHelper.getInstance().stopMusic()
+                countDownTimer?.cancel()
+                //todo 给服务器发送转接 tcp
+                val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
+                val voiceTransferTcpModel = VoiceUtil.voiceTransfer(Integer.parseInt(Constants.ids), tcpModel.fromId, interactionVO)
+                TcpClient.getInstance().sendMsg(voiceTransferTcpModel.toJson())
+                finish()
+            }
+            R.id.hang_up_imagev ->{
+                isAnswerOrHangUp = true
+                MediaPlayHelper.getInstance().stopMusic()
+                countDownTimer?.cancel()
+                //todo 给服务器发送拒接 tcp
+//            InteractionVO interactionVO = (InteractionVO) tcpModel.getData();
+                val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
+                val voiceUtilTcpModel = VoiceUtil.voiceReject(Integer.parseInt(Constants.ids), tcpModel.fromId, interactionVO.id)
+                TcpClient.getInstance().sendMsg(voiceUtilTcpModel.toJson())
+                 finish()
+            }
+            R.id.call_the_voice_imagev ->{
+                isAnswerOrHangUp = true
+                MediaPlayHelper.getInstance().stopMusic()
+                countDownTimer?.cancel()
+                //todo 给服务器发送接听 tcp
+//            InteractionVO interactionVO = (InteractionVO) tcpModel.getData();
+                val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
+                val voiceUtilTcpModel = VoiceUtil.voiceAccept(Integer.parseInt(Constants.ids), tcpModel.fromId, interactionVO.id)
+                TcpClient.getInstance().sendMsg(voiceUtilTcpModel.toJson())
+
+                val intent = Intent(this, SipVoipAudioActivity::class.java)
+                intent.putExtra("targetId", targetId)
+//            intent.putExtra("interactionVO", interactionVO);
+                intent.putExtra("TcpModel", tcpModel)
+                intent.putExtra(SipVoipAudioActivity().ACTION, SipVoipAudioActivity().CALLING)
+                startActivity(intent)
+                finish()
+
+            }
+
+        }
+
+    }
+
+    private fun initCountDownTimer() {
+
+        countdownTime = SettingConfig.getCountdownTime(this)
+        countDownTimer = object : CountDownTimer((countdownTime * 1000).toLong(), 1000) {
+            override fun onTick(l: Long) {
+
+            }
+
+            override fun onFinish() {
+                if (isAnswerOrHangUp) return
+                Log.e(TAG, "时间到 转发了")
+                MediaPlayHelper.getInstance().stopMusic()
+                //todo 给服务器发送转接 tcp
+                val interactionVO = Gson().fromJson(tcpModel.data.toString(), InteractionVO::class.java)
+                val voiceTransferTcpModel = VoiceUtil.voiceTransfer(Integer.parseInt(Constants.ids), tcpModel.fromId, interactionVO)
+                TcpClient.getInstance().sendMsg(voiceTransferTcpModel.toJson())
+                 finish()
+            }
+        }
+
+    }
+
+    override fun onStop() {
+        super.onStop()
+
+    }
+
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    fun onMoonEvent(messageEvent: MessageEvent) {
+        if (messageEvent.tag == 2) {
+            val tcpModel = messageEvent.getMessage() as TcpModel?
+            if (tcpModel!!.action === TcpAction.VoiceAction.CANCEL || tcpModel!!.action === TcpAction.VoiceAction.HANDOFF) {
+                //对方取消;
+                countDownTimer?.cancel()
+                MediaPlayHelper.getInstance().stopMusic()
+                finish()
+            }
+        }
+    }
+
+
+
+
+    override fun onDestroy() {
+        super.onDestroy()
+        isAnswerOrHangUp = false
+        if (countDownTimer != null) {
+            countDownTimer?.cancel()
+        }
+        MediaPlayHelper.getInstance().stopMusic()
+        EventBus.getDefault().unregister(this)
+    }
+}

+ 9 - 2
home/src/main/code/com/wdkl/ncs/android/component/home/activity/WatchCallRecordsActivity.kt

@@ -193,10 +193,17 @@ class WatchCallRecordsActivity : BaseActivity<WatchCallRecordsFragmentPresenter,
             //界面呈现
             AppTool.Time.delay(300){
                 var intent = Intent()
-                intent.setClass(this,RTCVoipAudioActivity::class.java)
+                 //rtc
+//                intent.setClass(this,RTCVoipAudioActivity::class.java)
+//                intent.putExtra("targetId", receivedData?.deviceSipId)
+//                intent.putExtra("TcpModel",tcpModel)
+//                intent.putExtra(RTCVoipAudioActivity.ACTION, RTCVoipAudioActivity.CALL)
+//                startActivity(intent)
+                 //sip
+                intent.setClass(this,SipVoipAudioActivity::class.java)
                 intent.putExtra("targetId", receivedData?.deviceSipId)
                 intent.putExtra("TcpModel",tcpModel)
-                intent.putExtra(RTCVoipAudioActivity.ACTION, RTCVoipAudioActivity.CALL)
+                intent.putExtra(SipVoipAudioActivity().ACTION, SipVoipAudioActivity().CALL)
                 startActivity(intent)
             }
         }

+ 1 - 1
home/src/main/code/com/wdkl/ncs/android/component/home/activity/WatchContactsActivity.kt

@@ -134,7 +134,7 @@ class WatchContactsActivity : BaseActivity<WatchActivityPresenter, WatchContacts
 
     @Subscribe(threadMode = ThreadMode.MAIN)
     fun onMoonEvent(messageEvent: MessageEvent) {
-        var messageEvent = messageEvent.getMessage() as TcpModel
+//        var messageEvent = messageEvent.getMessage() as TcpModel
 //        if(messageEvent.getAction() === TcpAction.EventAction.KEY_CLICK){
 //            Log.e(TAG,"收到tcp消息")
 //        }

+ 57 - 0
home/src/main/code/com/wdkl/ncs/android/component/home/activity/WatchEventDetailActivity.kt

@@ -1,6 +1,8 @@
 package com.wdkl.ncs.android.component.home.activity
 
+import android.content.Intent
 import android.util.Log
+import android.view.View
 import android.widget.Toast
 import com.enation.javashop.android.jrouter.external.annotation.Autowired
 import com.enation.javashop.android.jrouter.external.annotation.Router
@@ -29,6 +31,7 @@ import com.wdkl.ncs.android.middleware.tcp.TcpClient
 import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel
 import com.wdkl.ncs.android.middleware.tcp.channel.EventUtil
 import com.wdkl.ncs.android.middleware.tcp.channel.ImUtil
+import com.wdkl.ncs.android.middleware.tcp.channel.VoiceUtil
 import com.wdkl.ncs.android.middleware.tcp.dto.TcpModel
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpAction
 import com.wdkl.ncs.android.middleware.tcp.enums.TcpType
@@ -84,6 +87,19 @@ class WatchEventDetailActivity: BaseActivity<WatchHomeActivityPresenter, WatchAc
                 finish()
 //            })
         }
+
+        call_relyout.setOnClickListener{
+            Log.e(TAG,"tcpModel 发送的 "+tcpModel?.toJson())
+            var tcpModel:TcpModel?= null
+            if(interactionVO?.actionDirectionType == 1){
+                tcpModel = VoiceUtil.voiceCall(Integer.parseInt(Constants.ids),interactionVO?.fromDeviceId)
+            }else{
+                tcpModel = VoiceUtil.voiceCall(Integer.parseInt(Constants.ids),interactionVO?.toDeviceId)
+            }
+
+            TcpClient.getInstance().sendMsg(tcpModel?.toJson())
+        }
+
         event_status_img.setOnClickListener {
             //event_status_img.isEnabled = false
             if (tcpModel?.type == TcpType.EVENT) {
@@ -146,6 +162,38 @@ class WatchEventDetailActivity: BaseActivity<WatchHomeActivityPresenter, WatchAc
                     event_status_img.isEnabled = false
                     event_status_img.setImageResource(com.starrtc.demo.R.drawable.icon_switch_on)
                 }
+            }else {
+                call_out_linlyout.visibility = View.GONE
+                call_relyout.visibility = View.VISIBLE
+                if(tcpModel?.type == TcpType.VOICE){
+                      //todo 腕表没有按主机这边设计 没数据
+                    if (interactionVO?.actionEnd != null) {
+                        //判断是呼入还是呼出 1 分机到主机 2主机到分机
+                        if(interactionVO?.actionDirectionType == 1){
+                            event_text.setText("语音呼入已接")
+                        }else if(interactionVO?.actionDirectionType == 2 || interactionVO?.actionDirectionType == 3){
+                            event_text.setText("语音呼出已接")
+                        }
+
+                    }else{
+                        //判断是呼入还是呼出 1 分机到主机 2主机到分机
+                        if(interactionVO?.actionDirectionType == 1){
+                            event_text.setText("语音呼入未接")
+                        }else if(interactionVO?.actionDirectionType == 2 || interactionVO?.actionDirectionType == 3){
+                            event_text.setText("语音呼出未接")
+                        }
+
+                    }
+
+//                    event_text.setText("语音呼叫")
+
+
+                }else if(tcpModel?.type == TcpType.VIDEO){
+
+                    event_text.setText("视频呼叫")
+                }else if(tcpModel?.type == TcpType.SOS){
+                    event_text.setText("SOS紧急呼叫")
+                }
             }
         }
     }
@@ -172,6 +220,15 @@ class WatchEventDetailActivity: BaseActivity<WatchHomeActivityPresenter, WatchAc
                     event_end_text.setText(TimeTransition().stampToDate(responseInteractionVO.actionEnd * 1000))
                 }
             }
+        }else if(messageEvent.tag == 1){
+           var tcpModel = messageEvent.getMessage() as TcpModel
+            var intent = Intent()
+            intent.setClass(this,SipVoipAudioActivity::class.java)
+            intent.putExtra("targetId", interactionVO?.fromSipId)
+            intent.putExtra("TcpModel",tcpModel)
+            intent.putExtra(SipVoipAudioActivity().ACTION, SipVoipAudioActivity().CALL)
+            startActivity(intent)
+
         }
     }
 }

+ 148 - 8
home/src/main/code/com/wdkl/ncs/android/component/home/activity/WatchHomeActivity.kt

@@ -14,8 +14,15 @@ import android.view.View
 import com.enation.javashop.android.jrouter.external.annotation.Router
 import com.enation.javashop.net.engine.model.NetState
 import com.google.gson.Gson
+import com.vvsip.ansip.IVvsipServiceListener
+import com.vvsip.ansip.VvsipCall
 import com.wdkl.ncs.android.component.home.R
 import com.wdkl.ncs.android.component.home.SOSEmergencyCallActivity
+import com.wdkl.ncs.android.component.home.SipUtil.SipCallBack
+import com.wdkl.ncs.android.component.home.SipUtil.SipHelper
+import com.wdkl.ncs.android.component.home.SipUtil.SipHelperUtil
+import com.wdkl.ncs.android.component.home.SipUtil.SipHelperUtil2
+import com.wdkl.ncs.android.component.home.broadcast.BatteryBroadcastReceiver
 import com.wdkl.ncs.android.component.home.databinding.WatchActivityHomeBinding
 import com.wdkl.ncs.android.component.home.launch.HomeLaunch
 import com.wdkl.ncs.android.component.home.util.NetHelper
@@ -40,9 +47,12 @@ import com.wdkl.ncs.android.middleware.utils.MessageEvent
 import kotlinx.android.synthetic.main.watch_activity_home.*
 import org.greenrobot.eventbus.Subscribe
 import org.greenrobot.eventbus.ThreadMode
+import kotlin.math.log
 
 @Router(path = "/watch/home")
-class WatchHomeActivity : BaseActivity<WatchHomeActivityPresenter, WatchActivityHomeBinding>(), WatchHomeActivityContract.View, View.OnClickListener, View.OnLongClickListener {
+class WatchHomeActivity : BaseActivity<WatchHomeActivityPresenter, WatchActivityHomeBinding>(), WatchHomeActivityContract.View, View.OnClickListener, View.OnLongClickListener,
+        SipCallBack , IVvsipServiceListener {
+
     var TAG = WatchHomeActivity::class.java.getSimpleName()
 
     //监听网络变化
@@ -51,6 +61,9 @@ class WatchHomeActivity : BaseActivity<WatchHomeActivityPresenter, WatchActivity
     lateinit var wifiReceiver: WifiReceiver
     private var netType: Int = -1
 
+    lateinit var batteryBroadcastReceiver:BatteryBroadcastReceiver
+
+
     override fun getLayId(): Int {
         return R.layout.watch_activity_home
     }
@@ -66,6 +79,17 @@ class WatchHomeActivity : BaseActivity<WatchHomeActivityPresenter, WatchActivity
 
         watch_name_tv.text = Constants.user_name
 
+        if(Constants.user_role_name!!.contains("腕表")){
+            watch_role_name_tv.text = Constants.user_role_name!!.substring(0,(Constants.user_role_name)!!.indexOf("腕表"))
+        }else{
+            watch_role_name_tv.text = Constants.user_role_name
+        }
+
+         //开始ping网络,30秒ping一次
+        NetHelper.startNetCheck()
+        //SIP准备工作
+        initSip()
+
         wifiManager = (applicationContext.getSystemService(Context.WIFI_SERVICE)) as WifiManager
         teleManager = (applicationContext.getSystemService(Context.TELEPHONY_SERVICE)) as TelephonyManager
 
@@ -88,6 +112,12 @@ class WatchHomeActivity : BaseActivity<WatchHomeActivityPresenter, WatchActivity
         }
         regReceiver()
 
+        batteryBroadcastReceiver = BatteryBroadcastReceiver(electric_quantity_tv)
+
+        val intentFilter = IntentFilter()
+        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED)
+        registerReceiver(batteryBroadcastReceiver, intentFilter)
+
         //TTS初始化
         SpeechUtil.getInstance().init(BaseApplication.appContext)
         SpeechUtil.getInstance().startSpeechThread()
@@ -110,11 +140,17 @@ class WatchHomeActivity : BaseActivity<WatchHomeActivityPresenter, WatchActivity
         user_nume_linlyout.setOnClickListener(this)
         state_linlyout.setOnClickListener(this)
         user_nume_linlyout.setOnLongClickListener(this)
+        other_linyout.setOnClickListener(this)
     }
 
     override fun destory() {
         unRegReceiver()
         SpeechUtil.getInstance().release()
+        SipHelperUtil.getInstance(this).unRegisterSip()
+//        SipHelper.getInstance().unRegisterSip()
+        if(batteryBroadcastReceiver != null){
+            unregisterReceiver(batteryBroadcastReceiver)
+        }
     }
 
     override fun render() {
@@ -132,6 +168,58 @@ class WatchHomeActivity : BaseActivity<WatchHomeActivityPresenter, WatchActivity
     override fun networkMonitor(state: NetState) {
     }
 
+    private fun initSip() {
+
+
+        //=============================================Sip启动服务===================================//
+        SipHelperUtil.getInstance(this).sipStartService(Constants.sip_ip,Constants.sip_id,Constants.sip_password)
+        //=============================================SIP状态回调===================================//
+        SipHelperUtil.getInstance(this).setSipCallBack(this)
+        //=============================================SIP服务监听===================================//
+        SipHelperUtil.getInstance(this).obtainSipInfo()
+
+//        SipHelper.getInstance().sipStartService()
+//        SipHelper.getInstance().initSip(this, Constants.sip_ip,Constants.sip_id,Constants.sip_password)
+//        SipHelper.getInstance().setSipCallBack(this)
+//        SipHelper.getInstance().setSipListner(this)
+    }
+
+
+    /**
+     * sip开始通话    ------------------sip回调操作----------------------
+     */
+    override fun startCall(sipAddress: String?) {
+        SipHelperUtil.getInstance(this).startCall(sipAddress)
+    }
+
+    /**
+     * sip自动接听
+     */
+    override fun autoTalking() {
+        SipHelperUtil.getInstance(this).autoTalking()
+    }
+
+    /**
+     * sip结束通话
+     */
+    override fun endCall() {
+        SipHelperUtil.getInstance(this).endCall()
+    }
+
+    override fun onNewVvsipCallEvent(call: VvsipCall?) {
+        SipHelperUtil.getInstance(this).addCallObject(call)
+    }
+
+    override fun onRemoveVvsipCallEvent(call: VvsipCall?) {
+        SipHelperUtil.getInstance(this).removeCallObject(call)
+    }
+
+    override fun onStatusVvsipCallEvent(call: VvsipCall?) {
+    }
+
+    override fun onRegistrationEvent(rid: Int, remote_uri: String?, code: Int, reason: String?) {
+    }
+
     /**
      * 监听
      */
@@ -159,15 +247,21 @@ class WatchHomeActivity : BaseActivity<WatchHomeActivityPresenter, WatchActivity
 
             }
 
-
+            R.id.other_linyout ->{
+                val intent = Intent(this, WatchUserSettingActivity::class.java)
+                startActivity(intent)
+            }
         }
     }
 
+    /**
+     * 长按进入
+     */
     override fun onLongClick(p0: View): Boolean {
         when (p0.id) {
             R.id.user_nume_linlyout ->{
-                val intent = Intent(this, WatchUserSettingActivity::class.java)
-                startActivity(intent)
+//                val intent = Intent(this, WatchUserSettingActivity::class.java)
+//                startActivity(intent)
             }
         }
         return true
@@ -179,10 +273,41 @@ class WatchHomeActivity : BaseActivity<WatchHomeActivityPresenter, WatchActivity
 
     @Subscribe(threadMode = ThreadMode.MAIN)
     fun onMoonEvent(messageEvent: MessageEvent) {
-        //唤醒屏幕
-        Util().wakeUpAndUnlock(this)
+        if(messageEvent.tag == Constants.EVENT_SIP_REGISTER_STATUS){
+            var message = messageEvent.getMessage() as String
+            Log.e(TAG,"收到sip註冊消息 "+ message)
+            runOnUiThread {
+                if(message.equals(SipHelperUtil.REGISTERING)){
+                    sip_state_tv.setBackgroundColor(Color.parseColor("#FFFF00"))
+                }else if(message.equals(SipHelperUtil.REGISTERFAIL)){
+                    sip_state_tv.setBackgroundColor(Color.parseColor("#FF0000"))
+                }else if(message.equals(SipHelperUtil.REGISTERCOM)){
+                    sip_state_tv.setBackgroundColor(Color.parseColor("#00FFFF"))
+                }
+            }
+        }else if(messageEvent.tag == Constants.EVENT_INTERNETPING){
+            SipHelperUtil.getInstance(this).obtainSipInfo()
+
+        }else if(messageEvent.tag == Constants.EVENT_WIFI_RSSI){
+            var message = messageEvent.getMessage() as Int
+            if(10< message && message < 20){
+                electric_quantity_tv.setTextColor(Color.parseColor("#FF3030"))
+                battery_warning_tv.visibility = View.VISIBLE
+                battery_warning_tv.text = "低电量,请注意充电"
+
+            }else if(10 > message){
+                electric_quantity_tv.setTextColor(Color.parseColor("#8B2323"))
+                battery_warning_tv.visibility = View.VISIBLE
+                battery_warning_tv.text = "电量过低,请充电"
+            }else{
+                battery_warning_tv.visibility = View.GONE
+                electric_quantity_tv.text = ""+message
+            }
 
-        if (messageEvent.tag == 0) {
+
+        }else if (messageEvent.tag == 0) {
+            //唤醒屏幕并解锁屏幕
+            Util().wakeUpAndUnlock(this)
 
             var tcpModel = messageEvent.getMessage() as TcpModel
             var responseTcpModel: TcpModel
@@ -203,13 +328,21 @@ class WatchHomeActivity : BaseActivity<WatchHomeActivityPresenter, WatchActivity
                         //给服务器发送正在通话中 tcp
                         TcpClient.getInstance().sendMsg(responseTcpModel.toJson())
                     } else {
+                        Log.e(TAG,"启动接听界面")
                         responseTcpModel = VoiceUtil.voiceSuccess(tcpModel.getToId(), tcpModel.getFromId(), interactionVO.getId())
                         TcpClient.getInstance().sendMsg(responseTcpModel.toJson())
 
                         //界面呈现
                         AppTool.Time.delay(300) {
+//                            var intent = Intent()
+//                            intent.setClass(this, RTCVoipAudioRingingActivity::class.java)
+//                            intent.putExtra("targetId", interactionVO.fromSipId)
+//                            intent.putExtra("TcpModel", tcpModel)
+//                            startActivity(intent)
+
+                             //sip
                             var intent = Intent()
-                            intent.setClass(this, RTCVoipAudioRingingActivity::class.java)
+                            intent.setClass(this, SipVoipAudioRingingActivity::class.java)
                             intent.putExtra("targetId", interactionVO.fromSipId)
                             intent.putExtra("TcpModel", tcpModel)
                             startActivity(intent)
@@ -252,14 +385,21 @@ class WatchHomeActivity : BaseActivity<WatchHomeActivityPresenter, WatchActivity
                 if (wifiManager != null && netType == NetHelper.NETWORK_WIFI) {
                     val wifiInfo = wifiManager.getConnectionInfo()
                     val wifi = wifiInfo.getRssi ();//获取wifi信号强度
+                    Log.e(TAG,"wifi 信号强度"+wifi)
+
                     if (wifi > -50 && wifi < 0) {//最强
 //                        Log.e(TAG, "wzlll 最强")
+                        wifi_rssi_tv.text = "较强"
                     } else if (wifi > -70 && wifi < -50) {//较强
 //                        Log.e(TAG, "wzlll 较强")
+                        wifi_rssi_tv.text = "一般"
                     } else if (wifi > -80 && wifi < -70) {//较弱
 //                        Log.e(TAG, "wzlll 较弱")
+                        wifi_rssi_tv.text = "微弱"
                     } else if (wifi > -100 && wifi < -80) {//微弱
 //                        Log.e(TAG, "wzlll 微弱")
+                        wifi_rssi_tv.setTextColor(Color.parseColor("#FF3030"))
+                        wifi_rssi_tv.text = "wifi信号较弱,请注意"
                     }
                 }
             }

+ 25 - 1
home/src/main/code/com/wdkl/ncs/android/component/home/activity/WatchRegisterActivity.kt

@@ -6,6 +6,7 @@ import android.content.pm.PackageManager
 import android.os.Handler
 import android.support.v4.app.ActivityCompat
 import android.support.v4.content.ContextCompat
+import android.text.TextUtils
 import android.util.Log
 import com.enation.javashop.android.jrouter.external.annotation.Router
 import com.enation.javashop.net.engine.model.NetState
@@ -88,6 +89,12 @@ class WatchRegisterActivity : BaseActivity<WatchDevicePresenter, WatchActivityRe
             run {
                 //TcpClient.getInstance().init("192.168.1.138", 5080, 9)
                 //TcpClient.getInstance().init("47.106.200.55", 5080, 9)
+                if( Constants.heart_beat>40){
+                    Constants.heart_beat =  Constants.heart_beat-10
+                }else{
+                    Constants.heart_beat =  Constants.heart_beat
+                }
+
                 TcpClient.getInstance().init(Constants.tcp_server, Constants.tcp_port, Constants.heart_beat)
             }
         }).start()
@@ -98,7 +105,23 @@ class WatchRegisterActivity : BaseActivity<WatchDevicePresenter, WatchActivityRe
         Constants.ids = "" + data.id
         Constants.sip_id = data.sipId
         Constants.eth_ip = "192.168.1.138:8006/"
+        Constants.sip_ip = "192.168.1.162"
+        Constants.sip_id = data.sipId
+        Constants.sip_password = data.sipPassword
         Constants.user_name = data.memberName
+        Constants.user_role_name = data.name
+
+        if(TextUtils.isEmpty(Constants.ids)||TextUtils.isEmpty(Constants.sip_id)||TextUtils.isEmpty(Constants.user_name)
+                || TextUtils.isEmpty(Constants.user_role_name)){
+            showMessage("初始化数据时服务器返回数据不全,请联系管理员")
+            tv_feedback_device_info.text = "初始化数据时服务器返回数据不全,请联系管理员"
+            return
+        }else if (TextUtils.isEmpty(Constants.tcp_server)){
+            showMessage("初始化tcp连接数据为null")
+            tv_feedback_device_info.text = "初始化tcp连接数据为null"
+            return
+        }
+
         //这里的是视频通话有关的
         AEvent.setHandler(Handler())
         //这里的是视频通话有关的
@@ -107,6 +130,7 @@ class WatchRegisterActivity : BaseActivity<WatchDevicePresenter, WatchActivityRe
         initSDK(data.sipId)
 
         toHome()
+        showMessage("初始化完成")
     }
 
     private fun initSDK(DeviceSipId: String) {
@@ -175,7 +199,7 @@ class WatchRegisterActivity : BaseActivity<WatchDevicePresenter, WatchActivityRe
 
     @Subscribe(threadMode = ThreadMode.MAIN)
     fun onMoonEvent(messageEvent: MessageEvent) {
-        var messageEvent = messageEvent.getMessage() as TcpModel
+//        var messageEvent = messageEvent.getMessage() as TcpModel
 //        if(messageEvent.getAction() === TcpAction.EventAction.KEY_CLICK){
 //            Log.e(TAG,"收到tcp消息")
 //        }

+ 7 - 0
home/src/main/code/com/wdkl/ncs/android/component/home/activity/WatchUserSettingActivity.java

@@ -30,6 +30,7 @@ public class WatchUserSettingActivity extends Activity {
     private TextView tv_device_imei;
     private TextView tvDeviceIp;
     private TextView tvDeviceUser;
+    private TextView tv_device_user_role_name;
     private Button btnChange;
     private TextView countdown_time_tv;
     private SeekBar countdown_time_seekb;
@@ -46,6 +47,7 @@ public class WatchUserSettingActivity extends Activity {
         tv_device_imei = findViewById(R.id.tv_device_imei);
         tvDeviceIp = findViewById(R.id.tv_device_ip);
         tvDeviceUser = findViewById(R.id.tv_device_user_name);
+        tv_device_user_role_name = findViewById(R.id.tv_device_user_role_name);
         btnChange = findViewById(R.id.btn_user_change);
         countdown_time_tv = findViewById(R.id.countdown_time_tv);
         countdown_time_seekb = findViewById(R.id.countdown_time_seekb);
@@ -56,6 +58,11 @@ public class WatchUserSettingActivity extends Activity {
         tv_device_imei.setText("设备IMEI: " + Constants.Companion.getImei());
         tvDeviceIp.setText("设备IP: " + NetHelper.getInstance().getLocalIP());
         tvDeviceUser.setText("当前用户: " + Constants.Companion.getUser_name());
+        if(Constants.Companion.getUser_role_name().contains("腕表")){
+            tv_device_user_role_name.setText( "用户角色: " +Constants.Companion.getUser_role_name().substring(0,(Constants.Companion.getUser_role_name()).indexOf("腕表")));
+        }else{
+            tv_device_user_role_name.setText("用户角色: " + Constants.Companion.getUser_role_name());
+        }
 
         //设置呼叫转接时间
         countdown_time_tv.setText("转接时间: " + SettingConfig.getCountdownTime(this) + "秒");

+ 38 - 0
home/src/main/code/com/wdkl/ncs/android/component/home/broadcast/BatteryBroadcastReceiver.java

@@ -0,0 +1,38 @@
+package com.wdkl.ncs.android.component.home.broadcast;
+
+import android.annotation.SuppressLint;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.widget.TextView;
+
+import com.wdkl.ncs.android.component.nursehome.common.Constants;
+import com.wdkl.ncs.android.middleware.utils.MessageEvent;
+
+import org.greenrobot.eventbus.EventBus;
+
+
+public class BatteryBroadcastReceiver extends BroadcastReceiver {
+private String TAG = BatteryBroadcastReceiver.class.getSimpleName();
+
+    private TextView mBatteryView;
+
+    public BatteryBroadcastReceiver(TextView mBatteryView) {
+        this.mBatteryView = mBatteryView;
+    }
+
+    @SuppressLint("LongLogTag")
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
+            int level = intent.getIntExtra("level", 0);
+            int scale = intent.getIntExtra("scale", 100);
+            int power = level * 100 / scale;
+           Log.e(TAG, "电池电量::" + power);
+//            mBatteryView.setText(power);
+
+            EventBus.getDefault().post(new MessageEvent(power, Constants.Companion.getEVENT_WIFI_RSSI()));
+        }
+    }
+}

+ 14 - 0
home/src/main/code/com/wdkl/ncs/android/component/home/util/EthernetWifiCallBack.java

@@ -0,0 +1,14 @@
+package com.wdkl.ncs.android.component.home.util;
+
+/**
+ * ======================以太网和wifi状态接口=====================
+ * Created by dengzhe on 2018/2/7.
+ */
+
+public interface EthernetWifiCallBack {
+
+    boolean ethernetStatus(boolean status);//以太网状态
+
+    boolean wifiStatus(boolean status);//wifi状态
+
+}

+ 6 - 2
home/src/main/code/com/wdkl/ncs/android/component/home/util/NetHelper.java

@@ -10,7 +10,11 @@ import android.os.Build;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
+import com.wdkl.ncs.android.component.nursehome.common.Constants;
 import com.wdkl.ncs.android.lib.base.BaseApplication;
+import com.wdkl.ncs.android.middleware.utils.MessageEvent;
+
+import org.greenrobot.eventbus.EventBus;
 
 import java.io.BufferedReader;
 import java.io.IOException;
@@ -32,7 +36,7 @@ public class NetHelper {
     private static NetHelper sInstance = null;
 
     private static Timer timerNetStatus = null;
-    private static final int SCHEDULE_TIME = 30000;
+    private static final int SCHEDULE_TIME = 20000;
     public static boolean NetConn = false;
 
     public static final int NETWORK_NONE = -1;
@@ -76,7 +80,7 @@ public class NetHelper {
             public void run() {
                 NetConn = ping(getLocalElement(3), 2, null);
 
-//                EventBus.getDefault().post(new MessageEvent(ETHERNETSTATUS, EVENT_INTERNETPING));//循环检测SIP,以太网ping状态
+                EventBus.getDefault().post(new MessageEvent(ETHERNETSTATUS, Constants.Companion.getEVENT_INTERNETPING()));//循环检测SIP,以太网ping状态
             }
         }, 10, SCHEDULE_TIME);
     }

+ 76 - 0
home/src/main/res/layout/activity_sip_voip_audio.xml

@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#FFE6E8">
+
+    <LinearLayout
+        android:id="@+id/ll_name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerHorizontal="true"
+        android:gravity="center_horizontal"
+        android:layout_marginTop="11px"
+        android:orientation="vertical">
+
+        <ImageView
+            android:id="@+id/bao_mother_image_imagev"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/bao_ma_tou_xiang" />
+        <TextView
+            android:id="@+id/room_number_tv"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginTop="8px"
+            android:text="房号"
+            android:textSize="18sp" />
+        <TextView
+            android:id="@+id/bao_mother_name_tv"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginTop="8px"
+            android:text="宝妈名字"
+            android:textSize="20sp" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/ll_name"
+        android:layout_alignParentBottom="true"
+        android:layout_centerHorizontal="true"
+        android:layout_marginBottom="11px"
+        android:orientation="vertical">
+
+        <Chronometer
+            android:id="@+id/call_duration_tv"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:text="00:00"
+            android:textSize="12sp"
+            android:visibility="gone"/>
+
+        <TextView
+            android:id="@+id/voice_status"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginTop="8px"
+            android:textSize="12sp" />
+
+        <ImageView
+            android:id="@+id/hang_up_imagev"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="13px"
+            android:src="@drawable/yu_yin_gua_duan" />
+
+    </LinearLayout>
+
+
+</RelativeLayout>

+ 105 - 0
home/src/main/res/layout/activity_sip_voip_audio_ringing.xml

@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#FFE6E8">
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="11px"
+        android:gravity="center_horizontal"
+        android:orientation="vertical">
+
+        <ImageView
+            android:id="@+id/bao_mother_image_imagev"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/bao_ma_tou_xiang" />
+        <TextView
+            android:id="@+id/room_number_tv"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginTop="8px"
+            android:text="房号"
+            android:textSize="18sp" />
+        <TextView
+            android:id="@+id/bao_mother_name_tv"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginTop="8px"
+            android:text="宝妈名字"
+            android:textSize="20sp" />
+
+        <TextView
+            android:id="@+id/event_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginTop="8px"
+            android:text=""
+            android:textSize="20sp" />
+
+    </LinearLayout>
+
+
+    <TextView
+        android:id="@+id/call_duration_tv"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_above="@+id/call_out_linlyout"
+        android:layout_centerHorizontal="true"
+        android:text=""
+        android:textSize="24sp"
+        android:visibility="gone" />
+
+    <LinearLayout
+        android:id="@+id/call_out_linlyout"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="13px"
+        android:layout_marginBottom="11px"
+        android:orientation="horizontal">
+        <RelativeLayout
+            android:id="@+id/change_over_relayout"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="28px"
+            android:layout_gravity="center" >
+            <ImageView
+                android:id="@+id/change_over_image"
+                android:layout_width="45px"
+                android:layout_height="45px"
+                android:src="@drawable/yu_yin_wei_chu_li" />
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                android:textSize="14px"
+                android:textColor="#ffffff"
+                android:text="转接"/>
+
+        </RelativeLayout>
+        <ImageView
+            android:id="@+id/hang_up_imagev"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="28px"
+            android:src="@drawable/yu_yin_gua_duan"
+            android:visibility="gone"/>
+
+        <ImageView
+            android:id="@+id/call_the_voice_imagev"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/yu_yin_jie_ting" />
+
+    </LinearLayout>
+
+
+</RelativeLayout>

+ 8 - 2
home/src/main/res/layout/user_setting_layout.xml

@@ -47,10 +47,16 @@
                     android:textSize="16dp" />
 
                 <TextView
-                    android:id="@+id/tv_device_user_name"
+                android:id="@+id/tv_device_user_name"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="当前用户: 王护士"
+                android:textSize="16dp" />
+                <TextView
+                    android:id="@+id/tv_device_user_role_name"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:text="当前用户: 王护士"
+                    android:text="用户角色: 护士"
                     android:textSize="16dp" />
             </LinearLayout>
 

+ 24 - 4
home/src/main/res/layout/watch_activity_event_detail.xml

@@ -59,10 +59,29 @@
         android:textSize="14sp"
         android:visibility="gone"/>
 
+
+
+    <RelativeLayout
+        android:id="@+id/call_relyout"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_centerHorizontal="true"
+        android:layout_marginBottom="16dp"
+        android:visibility="gone">
+
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            android:background="@drawable/yu_yin_jie_ting" />
+    </RelativeLayout>
+
+
     <LinearLayout
         android:id="@+id/call_out_linlyout"
         android:layout_width="match_parent"
-        android:layout_height="54dp"
+        android:layout_height="65dp"
         android:layout_alignParentBottom="true"
         android:layout_centerHorizontal="true"
         android:layout_marginTop="13px"
@@ -78,6 +97,7 @@
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
             android:orientation="vertical"
             android:gravity="right">
             <TextView
@@ -88,10 +108,10 @@
                 android:text=""/>
             <ImageView
                 android:id="@+id/event_status_img"
-                android:layout_width="60px"
-                android:layout_height="30px"
+                android:layout_width="80px"
+                android:layout_height="35px"
                 android:layout_marginLeft="20px"
-                android:layout_marginRight="0dp"
+                android:layout_marginRight="5dp"
                 android:src="@drawable/icon_switch_off" />
         </LinearLayout>
 

+ 36 - 1
home/src/main/res/layout/watch_activity_home.xml

@@ -93,6 +93,14 @@
                         android:text="王护士"
                         android:textColor="#ffffff"
                         android:textSize="16px" />
+                    <TextView
+                        android:id="@+id/watch_role_name_tv"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="10px"
+                        android:text="护士"
+                        android:textColor="#ffffff"
+                        android:textSize="16px" />
 
                 </LinearLayout>
 
@@ -115,7 +123,8 @@
                             android:layout_height="match_parent"
                             android:layout_weight="1"
                             android:background="#EA89C0"
-                            android:gravity="center">
+                            android:gravity="center"
+                            android:orientation="vertical">
 
                             <ImageView
                                 android:id="@+id/network_state_imagev"
@@ -123,6 +132,13 @@
                                 android:layout_height="wrap_content"
                                 android:src="@drawable/wifi_lian_jie" />
 
+                            <TextView
+                                android:id="@+id/wifi_rssi_tv"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:text="100%"
+                                android:textColor="#ffffff"
+                                android:textSize="16px" />
                         </LinearLayout>
 
 
@@ -171,21 +187,40 @@
                                 android:text="100%"
                                 android:textColor="#ffffff"
                                 android:textSize="16px" />
+                            <TextView
+                                android:id="@+id/battery_warning_tv"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:text=""
+                                android:textColor="#ffffff"
+                                android:textSize="16px"
+                                android:visibility="gone"/>
                         </LinearLayout>
 
 
                         <LinearLayout
+                            android:id="@+id/other_linyout"
                             android:layout_width="0dp"
                             android:layout_height="match_parent"
                             android:layout_weight="1"
                             android:background="#FFDE00"
+                            android:orientation="vertical"
                             android:gravity="center">
 
+                            <TextView
+                                android:id="@+id/sip_state_tv"
+                                android:layout_width="8dp"
+                                android:layout_height="8dp"
+
+                                android:background="#FF0000"/>
                             <ImageView
                                 android:layout_width="wrap_content"
                                 android:layout_height="wrap_content"
+                                android:layout_marginTop="2dp"
                                 android:src="@drawable/geng_duo" />
 
+
+
                         </LinearLayout>
 
                     </LinearLayout>

+ 7 - 0
home/src/main/res/layout/watch_activity_register.xml

@@ -19,5 +19,12 @@
         android:layout_marginTop="20dp"
         android:textSize="20dp"
         android:text="MAC地址: "/>
+    <TextView
+        android:id="@+id/tv_feedback_device_info"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="20dp"
+        android:textSize="20dp"
+        android:text=""/>
 </LinearLayout>
 </layout>

+ 4 - 4
home/src/main/res/layout/watch_activity_voice_calls.xml

@@ -59,9 +59,11 @@
         android:layout_marginBottom="11px"
         android:orientation="horizontal">
       <RelativeLayout
+          android:id="@+id/change_over_relayout"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
-          android:layout_gravity="center_vertical">
+          android:layout_marginRight="28px"
+          android:layout_gravity="center" >
         <ImageView
             android:id="@+id/change_over_image"
             android:layout_width="45px"
@@ -80,14 +82,12 @@
             android:id="@+id/hang_up_imagev"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginLeft="28px"
+            android:layout_marginRight="28px"
             android:src="@drawable/yu_yin_gua_duan" />
-
         <ImageView
             android:id="@+id/call_the_voice_imagev"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginLeft="28px"
             android:src="@drawable/yu_yin_jie_ting" />
 
     </LinearLayout>

+ 1 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/api/UrlManager.kt

@@ -58,6 +58,7 @@ private class DevUrlManager : UrlManager{
 //        get() = "http://192.168.1.139"
 //get() = "http://47.106.200.55"
 //        get() = "http://dev.base.wdklian.com"
+//get() = "http://8.129.220.143"
 
     override val device_url: String
 //        get() = "${base}:6005/"

+ 0 - 0
middleware/src/main/code/com/wdkl/ncs/android/middleware/common/Constants.kt


Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov