├── .gradle
├── 4.4
│ ├── fileChanges
│ │ └── last-build.bin
│ ├── fileContent
│ │ └── fileContent.lock
│ ├── fileHashes
│ │ ├── fileHashes.bin
│ │ ├── fileHashes.lock
│ │ └── resourceHashesCache.bin
│ ├── javaCompile
│ │ ├── classAnalysis.bin
│ │ ├── jarAnalysis.bin
│ │ ├── javaCompile.lock
│ │ ├── taskHistory.bin
│ │ └── taskJars.bin
│ └── taskHistory
│ │ ├── taskHistory.bin
│ │ └── taskHistory.lock
└── buildOutputCleanup
│ ├── buildOutputCleanup.lock
│ ├── cache.properties
│ └── outputFiles.bin
├── .idea
├── caches
│ └── build_file_checksums.ser
├── codeStyles
│ └── Project.xml
├── gradle.xml
├── libraries
│ ├── Gradle__com_android_support_appcompat_v7_23_1_1.xml
│ ├── Gradle__com_android_support_design_23_1_1.xml
│ ├── Gradle__com_android_support_recyclerview_v7_23_1_1.xml
│ ├── Gradle__com_android_support_support_annotations_23_1_1_jar.xml
│ ├── Gradle__com_android_support_support_v4_23_1_1.xml
│ ├── Gradle__de_measite_minidns_minidns_0_1_7_jar.xml
│ ├── Gradle__io_pristine_libjingle_9127.xml
│ ├── Gradle__org_igniterealtime_smack_smack_android_4_1_0_jar.xml
│ ├── Gradle__org_igniterealtime_smack_smack_android_extensions_4_1_0_jar.xml
│ ├── Gradle__org_igniterealtime_smack_smack_core_4_1_0_jar.xml
│ ├── Gradle__org_igniterealtime_smack_smack_extensions_4_1_0_jar.xml
│ ├── Gradle__org_igniterealtime_smack_smack_im_4_1_0_jar.xml
│ ├── Gradle__org_igniterealtime_smack_smack_resolver_minidns_4_1_0_jar.xml
│ ├── Gradle__org_igniterealtime_smack_smack_sasl_provided_4_1_0_jar.xml
│ ├── Gradle__org_igniterealtime_smack_smack_tcp_4_1_0_jar.xml
│ ├── Gradle__org_jxmpp_jxmpp_core_0_4_2_beta1_jar.xml
│ ├── Gradle__org_jxmpp_jxmpp_util_cache_0_4_2_beta1_jar.xml
│ └── Gradle__xpp3_xpp3_1_1_4c_jar.xml
├── misc.xml
├── modules.xml
├── runConfigurations.xml
├── vcs.xml
└── workspace.xml
├── .svn
├── entries
├── format
├── wc.db
└── wc.db-journal
├── README.md
├── app
├── .gitignore
├── app.iml
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── example
│ │ └── activity
│ │ └── ApplicationTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ ├── CustomXML
│ │ │ ├── IceCandidateExtensionElement.java
│ │ │ ├── SDPExtensionElement.java
│ │ │ └── VideoInvitation.java
│ │ │ ├── MyApplication.java
│ │ │ ├── activity
│ │ │ ├── AddFriendActivity.java
│ │ │ ├── ChatActivity.java
│ │ │ ├── ChatServiceActivity.java
│ │ │ ├── LoginActivity.java
│ │ │ ├── MainActivity.java
│ │ │ ├── MultiUserChatRoomActivity.java
│ │ │ ├── MultiUserChatRoomsActivity.java
│ │ │ ├── ParentActivity.java
│ │ │ ├── RegisterActivity.java
│ │ │ └── VideoCallActivity.java
│ │ │ ├── adapter
│ │ │ ├── ChatListAdapter.java
│ │ │ ├── ChatServiceListAdapter.java
│ │ │ ├── FriendListAdapter.java
│ │ │ ├── MultiUserChatRoomListAdapter.java
│ │ │ └── ParentAdapter.java
│ │ │ ├── common
│ │ │ ├── Const.java
│ │ │ ├── Storage.java
│ │ │ ├── Util.java
│ │ │ └── XMPPUtil.java
│ │ │ └── data
│ │ │ ├── ChatData.java
│ │ │ ├── ChatRoomData.java
│ │ │ ├── ChatServiceData.java
│ │ │ ├── DataWarehouse.java
│ │ │ ├── LoginData.java
│ │ │ └── UserData.java
│ └── res
│ │ ├── drawable
│ │ └── side_nav_bar.xml
│ │ ├── layout
│ │ ├── activity_add_friend.xml
│ │ ├── activity_chat.xml
│ │ ├── activity_chat_service.xml
│ │ ├── activity_face.xml
│ │ ├── activity_login.xml
│ │ ├── activity_main.xml
│ │ ├── activity_multi_user_chat_room.xml
│ │ ├── activity_multi_user_chat_rooms.xml
│ │ ├── activity_register.xml
│ │ ├── activity_video_call.xml
│ │ ├── chat_list_item.xml
│ │ ├── chat_service_item.xml
│ │ ├── friend_list_item.xml
│ │ └── multi_user_chat_room_item.xml
│ │ ├── menu
│ │ ├── activity_navigation_drawer.xml
│ │ ├── friend_list_context_menu.xml
│ │ ├── menu_chat.xml
│ │ ├── menu_chat_room.xml
│ │ ├── menu_main.xml
│ │ └── navigation.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── drawables.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── smack
│ └── ExampleUnitTest.java
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── local.properties
├── settings.gradle
└── webrtc_test.iml
/.gradle/4.4/fileChanges/last-build.bin:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gradle/4.4/fileContent/fileContent.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.gradle/4.4/fileContent/fileContent.lock
--------------------------------------------------------------------------------
/.gradle/4.4/fileHashes/fileHashes.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.gradle/4.4/fileHashes/fileHashes.bin
--------------------------------------------------------------------------------
/.gradle/4.4/fileHashes/fileHashes.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.gradle/4.4/fileHashes/fileHashes.lock
--------------------------------------------------------------------------------
/.gradle/4.4/fileHashes/resourceHashesCache.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.gradle/4.4/fileHashes/resourceHashesCache.bin
--------------------------------------------------------------------------------
/.gradle/4.4/javaCompile/classAnalysis.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.gradle/4.4/javaCompile/classAnalysis.bin
--------------------------------------------------------------------------------
/.gradle/4.4/javaCompile/jarAnalysis.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.gradle/4.4/javaCompile/jarAnalysis.bin
--------------------------------------------------------------------------------
/.gradle/4.4/javaCompile/javaCompile.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.gradle/4.4/javaCompile/javaCompile.lock
--------------------------------------------------------------------------------
/.gradle/4.4/javaCompile/taskHistory.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.gradle/4.4/javaCompile/taskHistory.bin
--------------------------------------------------------------------------------
/.gradle/4.4/javaCompile/taskJars.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.gradle/4.4/javaCompile/taskJars.bin
--------------------------------------------------------------------------------
/.gradle/4.4/taskHistory/taskHistory.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.gradle/4.4/taskHistory/taskHistory.bin
--------------------------------------------------------------------------------
/.gradle/4.4/taskHistory/taskHistory.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.gradle/4.4/taskHistory/taskHistory.lock
--------------------------------------------------------------------------------
/.gradle/buildOutputCleanup/buildOutputCleanup.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.gradle/buildOutputCleanup/buildOutputCleanup.lock
--------------------------------------------------------------------------------
/.gradle/buildOutputCleanup/cache.properties:
--------------------------------------------------------------------------------
1 | #Tue Sep 11 14:06:34 CST 2018
2 | gradle.version=4.4
3 |
--------------------------------------------------------------------------------
/.gradle/buildOutputCleanup/outputFiles.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.gradle/buildOutputCleanup/outputFiles.bin
--------------------------------------------------------------------------------
/.idea/caches/build_file_checksums.ser:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.idea/caches/build_file_checksums.ser
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__com_android_support_appcompat_v7_23_1_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__com_android_support_design_23_1_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__com_android_support_recyclerview_v7_23_1_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__com_android_support_support_annotations_23_1_1_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__com_android_support_support_v4_23_1_1.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__de_measite_minidns_minidns_0_1_7_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__io_pristine_libjingle_9127.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_igniterealtime_smack_smack_android_4_1_0_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_igniterealtime_smack_smack_android_extensions_4_1_0_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_igniterealtime_smack_smack_core_4_1_0_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_igniterealtime_smack_smack_extensions_4_1_0_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_igniterealtime_smack_smack_im_4_1_0_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_igniterealtime_smack_smack_resolver_minidns_4_1_0_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_igniterealtime_smack_smack_sasl_provided_4_1_0_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_igniterealtime_smack_smack_tcp_4_1_0_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_jxmpp_jxmpp_core_0_4_2_beta1_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__org_jxmpp_jxmpp_util_cache_0_4_2_beta1_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/libraries/Gradle__xpp3_xpp3_1_1_4c_jar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.svn/entries:
--------------------------------------------------------------------------------
1 | 12
2 |
--------------------------------------------------------------------------------
/.svn/format:
--------------------------------------------------------------------------------
1 | 12
2 |
--------------------------------------------------------------------------------
/.svn/wc.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.svn/wc.db
--------------------------------------------------------------------------------
/.svn/wc.db-journal:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/.svn/wc.db-journal
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | generateDebugSources
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/example/activity/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.example.activity;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
56 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/CustomXML/IceCandidateExtensionElement.java:
--------------------------------------------------------------------------------
1 | package com.example.CustomXML;
2 |
3 | import org.jivesoftware.smack.packet.ExtensionElement;
4 |
5 |
6 | public class IceCandidateExtensionElement implements ExtensionElement {
7 | public static final String NAME_SPACE = "com.webrtc.ice_candidate";
8 | public static final String ELEMENT_NAME = "ice_candidate";
9 |
10 | //代表public final String sdpMid;
11 | private String sdpMidElement = "sdpMid";
12 | private String sdpMidText = "";
13 |
14 | //代表public final int sdpMLineIndex;
15 | private String sdpMLineIndexElement = "sdpMLineIndex";
16 | private int sdpMLineIndexText = 0;
17 |
18 | //代表public final String sdp;
19 | private String sdpElement = "sdp";
20 | private String sdpText = "";
21 |
22 | public String getSdpMidText() {
23 | return sdpMidText;
24 | }
25 |
26 | public void setSdpMidText(String sdpMidText) {
27 | this.sdpMidText = sdpMidText;
28 | }
29 |
30 | public int getSdpMLineIndexText() {
31 | return sdpMLineIndexText;
32 | }
33 |
34 | public void setSdpMLineIndexText(int sdpMLineIndexText) {
35 | this.sdpMLineIndexText = sdpMLineIndexText;
36 | }
37 |
38 | public String getSdpText() {
39 | return sdpText;
40 | }
41 |
42 | public void setSdpText(String sdpText) {
43 | this.sdpText = sdpText;
44 | }
45 |
46 | @Override
47 | public String getNamespace() {
48 | return NAME_SPACE;
49 | }
50 |
51 | @Override
52 | public String getElementName() {
53 | return ELEMENT_NAME;
54 | }
55 |
56 | @Override
57 | public CharSequence toXML() {
58 | StringBuilder sb = new StringBuilder();
59 |
60 | /*
61 | * sdpMidText
62 | * sdpMLineIndexText
63 | * sdpText
64 | **/
65 | sb.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAME_SPACE).append("\">");
66 | sb.append("<" + sdpMidElement + ">").append(sdpMidText).append(""+sdpMidElement+">");
67 | sb.append("<" + sdpMLineIndexElement + ">").append(sdpMLineIndexText).append(""+sdpMLineIndexElement+">");
68 | sb.append("<" + sdpElement + ">").append(sdpText).append(""+sdpElement+">");
69 | sb.append(""+ELEMENT_NAME+">");
70 |
71 | return sb.toString();
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/CustomXML/SDPExtensionElement.java:
--------------------------------------------------------------------------------
1 | package com.example.CustomXML;
2 |
3 | import org.jivesoftware.smack.packet.ExtensionElement;
4 |
5 | public class SDPExtensionElement implements ExtensionElement {
6 | public static final String NAME_SPACE = "com.webrtc.sdp";
7 | public static final String ELEMENT_NAME = "sdp";
8 |
9 | //代表SessionDescription.Type type
10 | private String typeElement = "type";
11 | private String typeText = "";
12 |
13 | //代表 String description
14 | private String descriptionElement = "description";
15 | private String descriptionText = "";
16 |
17 | public String getTypeText() {
18 | return typeText;
19 | }
20 |
21 | public void setTypeText(String typeText) {
22 | this.typeText = typeText;
23 | }
24 |
25 | public String getDescriptionText() {
26 | return descriptionText;
27 | }
28 |
29 | public void setDescriptionText(String descriptionText) {
30 | this.descriptionText = descriptionText;
31 | }
32 |
33 | @Override
34 | public String getNamespace() {
35 | return NAME_SPACE;
36 | }
37 |
38 | @Override
39 | public String getElementName() {
40 | return ELEMENT_NAME;
41 | }
42 |
43 | @Override
44 | public CharSequence toXML() {
45 | StringBuilder sb = new StringBuilder();
46 |
47 | /*
48 | * typeText
49 | * descriptionText
50 | **/
51 | sb.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAME_SPACE).append("\">");
52 | sb.append("<" + typeElement + ">").append(typeText).append(""+typeElement+">");
53 | sb.append("<" + descriptionElement + ">").append(descriptionText).append(""+descriptionElement+">");
54 | sb.append(""+ELEMENT_NAME+">");
55 |
56 | return sb.toString();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/CustomXML/VideoInvitation.java:
--------------------------------------------------------------------------------
1 | package com.example.CustomXML;
2 |
3 | import org.jivesoftware.smack.packet.ExtensionElement;
4 |
5 |
6 | public class VideoInvitation implements ExtensionElement {
7 | public static final String NAME_SPACE = "com.webrtc.video";
8 | public static final String ELEMENT_NAME = "video-chat";
9 |
10 | private String typeElement = "type";
11 |
12 | private String typeText = "";
13 |
14 | @Override
15 | public String getNamespace() {
16 | return NAME_SPACE;
17 | }
18 |
19 | @Override
20 | public String getElementName() {
21 | return ELEMENT_NAME;
22 | }
23 |
24 | @Override
25 | public CharSequence toXML() {
26 | StringBuilder sb = new StringBuilder();
27 |
28 | /*
29 | * typeText
30 | **/
31 | sb.append("<").append(ELEMENT_NAME).append(" xmlns=\"").append(NAME_SPACE).append("\">");
32 | sb.append("<" + typeElement + ">").append(typeText).append(""+typeElement+">");
33 | sb.append(""+ELEMENT_NAME+">");
34 |
35 | return sb.toString();
36 | }
37 |
38 | public String getTypeText() {
39 | return typeText;
40 | }
41 |
42 | public void setTypeText(String typeText) {
43 | this.typeText = typeText;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/MyApplication.java:
--------------------------------------------------------------------------------
1 | package com.example;
2 |
3 | import android.Manifest;
4 | import android.app.Application;
5 | import android.content.pm.PackageManager;
6 | import android.support.v4.app.ActivityCompat;
7 |
8 | import com.example.data.LoginData;
9 |
10 | import org.jivesoftware.smack.tcp.XMPPTCPConnection;
11 |
12 | import java.util.Set;
13 | import java.util.TreeSet;
14 |
15 | public class MyApplication extends Application{
16 | public XMPPTCPConnection xmpptcpConnection;
17 | //记录登录信息
18 | public LoginData loginData = new LoginData();
19 | //记录当前正在聊天的用户
20 | public Set chatUsers = new TreeSet<>();
21 |
22 |
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/activity/AddFriendActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.activity;
2 |
3 | import android.content.Intent;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.os.Bundle;
6 | import android.view.View;
7 | import android.widget.Button;
8 | import android.widget.EditText;
9 | import android.widget.Toast;
10 |
11 | import org.jivesoftware.smack.roster.Roster;
12 |
13 | public class AddFriendActivity extends ParentActivity {
14 | private EditText mEditTextFriendUsername;
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.activity_add_friend);
20 |
21 | mEditTextFriendUsername = (EditText) findViewById(R.id.frendUsername);
22 |
23 |
24 | }
25 |
26 | public void onClickAddFriend(View view) {
27 | String account = mEditTextFriendUsername.getText().toString().trim();
28 |
29 | if("".equals(account))
30 | {
31 | Toast.makeText(this,"请输入账号",Toast.LENGTH_SHORT).show();
32 | return;
33 | }
34 |
35 |
36 | try {
37 | //添加好友
38 | Roster.getInstanceFor(connection).createEntry(account, "", null);
39 | Intent intent = new Intent();
40 | intent.putExtra("friendName",account);
41 | setResult(1, intent);
42 | Toast.makeText(this,"添加好友成功!",Toast.LENGTH_SHORT).show();
43 | finish();
44 | }
45 | catch (Exception e)
46 | {
47 | e.printStackTrace();
48 | Toast.makeText(this,"添加好友失败("+e.getMessage()+")",Toast.LENGTH_SHORT).show();
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/activity/ChatActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.activity;
2 |
3 | import android.content.Intent;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Matrix;
7 | import android.os.Bundle;
8 | import android.text.SpannableString;
9 | import android.text.Spanned;
10 | import android.text.style.ImageSpan;
11 | import android.util.Log;
12 | import android.view.Menu;
13 | import android.view.MenuItem;
14 | import android.view.View;
15 | import android.widget.EditText;
16 | import android.widget.ListView;
17 | import android.widget.Toast;
18 |
19 | import com.example.CustomXML.VideoInvitation;
20 | import com.example.adapter.ChatListAdapter;
21 | import com.example.common.Const;
22 | import com.example.common.Util;
23 | import com.example.data.ChatData;
24 | import com.example.data.DataWarehouse;
25 | import com.example.data.LoginData;
26 |
27 | import org.jivesoftware.smack.SmackException;
28 | import org.jivesoftware.smack.chat.Chat;
29 | import org.jivesoftware.smack.chat.ChatManager;
30 | import org.jivesoftware.smack.packet.Message;
31 |
32 | public class ChatActivity extends ParentActivity implements Const{
33 | protected String mFriendUsername; //聊天用户名称
34 | private String mServiceName; //XMPP服务器名称
35 |
36 | private LoginData mLoginData; //当前账号登录信息
37 |
38 | private EditText mEditTextChatText; //聊天文本框
39 | protected ListView mListViewChatList; //显示聊天记录
40 |
41 | protected ChatListAdapter mChatListAdapter;
42 |
43 | // private MyHandler myHandler;
44 |
45 | // Handler mHandler;
46 |
47 | @Override
48 | protected void onCreate(Bundle savedInstanceState) {
49 | super.onCreate(savedInstanceState);
50 | setContentView(R.layout.activity_chat);
51 |
52 | mServiceName = connection.getServiceName();
53 | Log.e("ServiceName","ChatActivity"+mServiceName);
54 |
55 | mLoginData = DataWarehouse.getGlobalData(this).loginData;
56 |
57 | mEditTextChatText = (EditText) findViewById(R.id.edittext_chat_text);
58 | mListViewChatList = (ListView) findViewById(R.id.listview_ChatList);
59 |
60 | mChatListAdapter = new ChatListAdapter(this);
61 | mListViewChatList.setAdapter(mChatListAdapter);
62 | //隐藏分隔条
63 | mListViewChatList.setDivider(null);
64 |
65 | //处理发送过来的聊天信息等。
66 | processExtraData();
67 | }
68 |
69 | @Override
70 | protected void onNewIntent(Intent intent) {
71 | super.onNewIntent(intent);
72 | //must store the new intent unless getIntent() will return the old one
73 | setIntent(intent);
74 | processExtraData();
75 | }
76 |
77 | private void processExtraData()
78 | {
79 | Intent intent = getIntent();
80 | if (mFriendUsername == null) {
81 | mFriendUsername = intent.getStringExtra("friendUsername");
82 | }
83 |
84 |
85 | String body = intent.getStringExtra("body");
86 | if (body != null)
87 | {
88 | ChatData item = new ChatData();
89 | item.text = body;
90 | item.user = mFriendUsername;
91 | mChatListAdapter.addItem(item);
92 | mListViewChatList.setSelection(mListViewChatList.getAdapter().getCount()-1);
93 | }
94 |
95 | DataWarehouse.getGlobalData(this).chatUsers.add(mFriendUsername);
96 | }
97 |
98 |
99 | @Override
100 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
101 | switch (requestCode)
102 | {
103 | case 1:
104 | int faceId = data.getIntExtra(KEY_FACE_ID, -1);
105 | if (faceId != -1)
106 | {
107 | String faceResName = FACE_PREFIX + faceId;
108 | Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
109 | Util.getResourceIDFromName(R.drawable.class,faceResName));
110 | //将表情缩小到原来的60%
111 | Matrix matrix = new Matrix();
112 | matrix.postScale(0.6f,0.6f);
113 | Bitmap smallBitmap = Bitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),
114 | bitmap.getHeight(),matrix,true);
115 | ImageSpan imageSpan = new ImageSpan(this,smallBitmap);
116 | //表情图片代表的表情文本,EditText接收到的表情是表情文本如<:10:>,要想显示
117 | //表情图片需要将表情文本转换为表情图片,转换函数为Util.updateFacesForTextView()
118 | String faceText = FACE_TEXT_PREFIX + faceId + FACE_TEXT_SUFFIX;
119 | SpannableString spannableString = new SpannableString(faceText);
120 | spannableString.setSpan(imageSpan, 0, faceText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
121 |
122 | //插入表情图片
123 | mEditTextChatText.getText().insert(mEditTextChatText.getSelectionStart(), spannableString);
124 |
125 | }
126 | break;
127 | }
128 | }
129 |
130 | public void onClick_Send(View view) {
131 | String chatText = mEditTextChatText.getText().toString().trim();
132 | if (!"".equals(chatText))
133 | {
134 | String chatJid = mFriendUsername+"@"+mServiceName;
135 | Message message = new Message(chatJid,Message.Type.chat);
136 | message.setBody(chatText);
137 | Chat chat = createChat(chatJid);
138 |
139 | try {
140 | chat.sendMessage(message);
141 | mEditTextChatText.setText("");
142 | } catch (SmackException.NotConnectedException e) {
143 | e.printStackTrace();
144 | }
145 | //显示发送聊天信息
146 | ChatData item = new ChatData();
147 | item.user = mLoginData.userName; //没有设置别名的功能。
148 | item.text = chatText;
149 | item.isOwner = true;
150 |
151 | mChatListAdapter.addItem(item);
152 | //滚动到消息的最后一条。
153 | mListViewChatList.setSelection(mListViewChatList.getAdapter().getCount()-1);
154 |
155 | }
156 | else
157 | {
158 | Toast.makeText(this,"发送消息不能为空",Toast.LENGTH_SHORT).show();
159 | }
160 |
161 | }
162 |
163 |
164 | @Override
165 | protected void onDestroy() {
166 | super.onDestroy();
167 | //当当前Activity销毁时,关闭监听对象。
168 | DataWarehouse.getGlobalData(this).chatUsers.remove(mFriendUsername);
169 | }
170 |
171 | @Override
172 | public boolean onCreateOptionsMenu(Menu menu) {
173 | super.onCreateOptionsMenu(menu);
174 | getMenuInflater().inflate(R.menu.menu_chat, menu);
175 | return true;
176 | }
177 |
178 | @Override
179 | public boolean onOptionsItemSelected(MenuItem item) {
180 | int id = item.getItemId();
181 |
182 | switch (id)
183 | {
184 | //向对方发送视频通话请求
185 | case R.id.request_video:
186 | String chatJid = mFriendUsername+"@"+mServiceName;
187 | Message message = new Message();
188 |
189 | VideoInvitation videoInvitation = new VideoInvitation();
190 | videoInvitation.setTypeText("video-invitation");
191 | message.addExtension(videoInvitation);
192 | Chat chat = createChat(chatJid);
193 |
194 | try {
195 | chat.sendMessage(message);
196 | } catch (SmackException.NotConnectedException e) {
197 | e.printStackTrace();
198 | }
199 | break;
200 | }
201 |
202 | return super.onOptionsItemSelected(item);
203 | }
204 |
205 | }
206 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/activity/ChatServiceActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.activity;
2 |
3 | import android.content.Intent;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.os.Bundle;
6 | import android.view.View;
7 | import android.widget.AdapterView;
8 | import android.widget.ListView;
9 |
10 | import com.example.adapter.ChatServiceListAdapter;
11 | import com.example.common.Const;
12 |
13 | import org.jivesoftware.smack.SmackException;
14 | import org.jivesoftware.smack.XMPPException;
15 | import org.jivesoftware.smackx.muc.HostedRoom;
16 | import org.jivesoftware.smackx.muc.MultiUserChat;
17 | import org.jivesoftware.smackx.muc.MultiUserChatManager;
18 |
19 | import java.util.ArrayList;
20 | import java.util.Collection;
21 | import java.util.List;
22 |
23 | public class ChatServiceActivity extends ParentActivity implements Const{
24 | private ListView listView;
25 | private ChatServiceListAdapter chatServiceListAdapter;
26 | @Override
27 | protected void onCreate(Bundle savedInstanceState) {
28 | super.onCreate(savedInstanceState);
29 | setContentView(R.layout.activity_chat_service);
30 |
31 | listView = (ListView) findViewById(R.id.listview_chat_services);
32 | chatServiceListAdapter = new ChatServiceListAdapter(this,getHostedRooms());
33 | listView.setAdapter(chatServiceListAdapter);
34 |
35 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
36 | @Override
37 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
38 | String jid = chatServiceListAdapter.getJID(position);
39 | Intent intent = new Intent(ChatServiceActivity.this,MultiUserChatRoomsActivity.class);
40 | intent.putExtra(JID,jid);
41 | startActivity(intent);
42 | }
43 | });
44 | }
45 |
46 | private List getHostedRooms()
47 | {
48 |
49 | List chatServices = new ArrayList<>();
50 |
51 | try {
52 | chatServices = MultiUserChatManager.getInstanceFor(connection).
53 | getHostedRooms(connection.getServiceName());
54 | } catch (SmackException.NoResponseException e) {
55 | e.printStackTrace();
56 | } catch (XMPPException.XMPPErrorException e) {
57 | e.printStackTrace();
58 | } catch (SmackException.NotConnectedException e) {
59 | e.printStackTrace();
60 | }
61 | return chatServices;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/activity/LoginActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.activity;
2 |
3 | import android.Manifest;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.pm.PackageManager;
7 | import android.os.Bundle;
8 | import android.os.Handler;
9 | import android.support.v4.app.ActivityCompat;
10 | import android.support.v7.app.AppCompatActivity;
11 | import android.util.Log;
12 | import android.view.View;
13 | import android.widget.CheckBox;
14 | import android.widget.EditText;
15 | import android.widget.Toast;
16 |
17 | import com.example.common.Const;
18 | import com.example.common.Storage;
19 | import com.example.common.XMPPUtil;
20 | import com.example.data.DataWarehouse;
21 | import com.example.data.LoginData;
22 |
23 | public class LoginActivity extends AppCompatActivity implements Const{
24 | private EditText mEditTextUsername;
25 | private EditText mEditTextPassword;
26 |
27 | private LoginData mLoginDate;
28 |
29 | private Handler handler;
30 | private Context context;
31 | @Override
32 | protected void onCreate(Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 |
35 | setContentView(R.layout.activity_login);
36 |
37 | context = this;
38 | mEditTextUsername = (EditText) findViewById(R.id.username);
39 | mEditTextPassword = (EditText) findViewById(R.id.password);
40 |
41 | mLoginDate = DataWarehouse.getGlobalData(this).loginData;
42 |
43 | handler = new Handler();
44 |
45 | mLoginDate.userName = Storage.getString(this,KEY_USERNAME);
46 | mLoginDate.passWord = Storage.getString(this,KEY_PASSWORD);
47 |
48 | mEditTextUsername.setText(mLoginDate.userName);
49 | mEditTextPassword.setText(mLoginDate.passWord);
50 |
51 | checkPermissions();
52 |
53 | }
54 |
55 | //登录按钮的单击事件方法
56 | public void onClickLogin(View view) {
57 | //将登录信息保存到全局对象中,当然也可以从SharedPreferences中读取,
58 | // 但是比较麻烦,所以保存到全局变量中。
59 | mLoginDate.userName = mEditTextUsername.getText().toString();
60 | mLoginDate.passWord = mEditTextPassword.getText().toString();
61 |
62 | if (mLoginDate.userName.equals(null) || mLoginDate.userName.equals(""))
63 | {
64 | Toast.makeText(LoginActivity.this,"请输入用户名",Toast.LENGTH_SHORT).show();
65 | return;
66 | }
67 | if (mLoginDate.passWord.equals(null) || mLoginDate.passWord.equals(""))
68 | {
69 | Toast.makeText(LoginActivity.this,"请输入密码",Toast.LENGTH_SHORT).show();
70 | return;
71 | }
72 | Log.e("username",mLoginDate.userName);
73 | Log.e("password",mLoginDate.passWord);
74 |
75 | //存储登录信息。
76 | /*为了存储的这些数据可以在别的地方应用,故需要将Key定义为常量,以便其他地方引用。
77 | *将Key保存在接口中以便其它类引用。
78 | * 当然,也可以定义在一个类中,但是需要将Key定义为静态变量,比较麻烦,故将其定义在接口中。
79 | * 将Key保存在接口Const中,所有的Key名称以Key_开头。
80 | */
81 | Storage.putString(this,KEY_USERNAME,mLoginDate.userName);
82 | Storage.putString(this,KEY_PASSWORD,mLoginDate.passWord);
83 |
84 | //新版本中不允许在主线程中直接访问网络。故登录服务器需要另起一个线程。
85 | new Thread(new Runnable() {
86 | @Override
87 | public void run() {
88 | //登录成功
89 | if (XMPPUtil.login(context,mLoginDate.userName,mLoginDate.passWord))
90 | {
91 |
92 | Intent intent = new Intent(LoginActivity.this,MainActivity.class);
93 | startActivity(intent);
94 | finish();
95 | }
96 | else //登录失败
97 | {
98 | handler.post(new Runnable() {
99 | @Override
100 | public void run() {
101 | Toast.makeText(LoginActivity.this,"登录失败,请检查用户名和密码的正确性",Toast.LENGTH_SHORT).show();
102 | }
103 | });
104 | }
105 | }
106 | }).start();
107 | }
108 |
109 | public void onClickRegister(View view) {
110 | Intent intent = new Intent(LoginActivity.this,RegisterActivity.class);
111 | startActivity(intent);
112 | }
113 |
114 | private void checkPermissions() {
115 |
116 | int REQUEST_EXTERNAL_STORAGE = 1;
117 | String[] PERMISSIONS_STORAGE = {
118 | Manifest.permission.READ_EXTERNAL_STORAGE,
119 | Manifest.permission.WRITE_EXTERNAL_STORAGE,
120 | Manifest.permission.RECORD_AUDIO,
121 | Manifest.permission.ACCESS_WIFI_STATE,
122 | Manifest.permission.INTERNET,
123 | Manifest.permission.ACCESS_NETWORK_STATE,
124 | Manifest.permission.ACCESS_WIFI_STATE,
125 | Manifest.permission.ACCESS_COARSE_LOCATION,
126 | Manifest.permission.CHANGE_WIFI_STATE,
127 | Manifest.permission.CAMERA,
128 | Manifest.permission.READ_PHONE_STATE,
129 | Manifest.permission.MODIFY_AUDIO_SETTINGS
130 | };
131 | int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
132 | if (permission != PackageManager.PERMISSION_GRANTED) {
133 | // We don't have permission so prompt the user
134 | ActivityCompat.requestPermissions(
135 | this,
136 | PERMISSIONS_STORAGE,
137 | REQUEST_EXTERNAL_STORAGE
138 | );
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/activity/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.activity;
2 |
3 | import android.app.AlertDialog;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 | import android.content.Intent;
7 | import android.os.Bundle;
8 | import android.os.Handler;
9 | import android.util.Log;
10 | import android.view.ContextMenu;
11 | import android.view.Menu;
12 | import android.view.MenuItem;
13 | import android.view.View;
14 | import android.widget.AdapterView;
15 | import android.widget.ListView;
16 | import android.widget.Toast;
17 |
18 | import com.example.CustomXML.IceCandidateExtensionElement;
19 | import com.example.CustomXML.SDPExtensionElement;
20 | import com.example.CustomXML.VideoInvitation;
21 | import com.example.adapter.FriendListAdapter;
22 | import com.example.common.Const;
23 | import com.example.common.Storage;
24 | import com.example.common.Util;
25 | import com.example.data.DataWarehouse;
26 | import com.example.data.LoginData;
27 | import com.example.data.UserData;
28 |
29 | import org.jivesoftware.smack.SmackException;
30 | import org.jivesoftware.smack.chat.Chat;
31 | import org.jivesoftware.smack.chat.ChatManagerListener;
32 | import org.jivesoftware.smack.chat.ChatMessageListener;
33 | import org.jivesoftware.smack.packet.DefaultExtensionElement;
34 | import org.jivesoftware.smack.packet.Message;
35 | import org.jivesoftware.smack.roster.Roster;
36 |
37 | import java.util.Set;
38 |
39 | public class MainActivity extends ParentActivity {
40 | private ListView mListViewFriend;
41 | private Set mChatUsers;
42 |
43 | public static FriendListAdapter mFriendListAdapter;
44 |
45 | private MyHandler myHandler;
46 | private int mCurrentPosition = -1;
47 | private String mServiceName;
48 |
49 |
50 | @Override
51 | protected void onCreate(Bundle savedInstanceState) {
52 | super.onCreate(savedInstanceState);
53 | setContentView(R.layout.activity_main);
54 |
55 | mServiceName = connection.getServiceName();
56 |
57 | mListViewFriend = (ListView) findViewById(R.id.listview_friends);
58 |
59 | mFriendListAdapter = new FriendListAdapter(this, Roster.getInstanceFor(connection).getEntries());
60 |
61 | mChatUsers = DataWarehouse.getGlobalData(this).chatUsers;
62 |
63 | mListViewFriend.setAdapter(mFriendListAdapter);
64 |
65 | //点击进入聊天窗口
66 | mListViewFriend.setOnItemClickListener(new AdapterView.OnItemClickListener() {
67 | @Override
68 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
69 | Intent intent = new Intent(MainActivity.this, ChatActivity.class);
70 | String friendUserame = mFriendListAdapter.getUsername(position);
71 | intent.putExtra("friendUsername", friendUserame);
72 | startActivity(intent);
73 | }
74 | });
75 |
76 | //设置聊天对象管理处理监听。
77 | getChatManager().addChatListener(chatManagerListenerMain);
78 |
79 | myHandler = new MyHandler(this);
80 |
81 | registerForContextMenu(mListViewFriend);
82 | mListViewFriend.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
83 | @Override
84 | public boolean onItemLongClick(AdapterView> parent, View view, int position, long id) {
85 | mCurrentPosition = position;
86 | return false;
87 | }
88 | });
89 | }
90 |
91 | @Override
92 | public boolean onContextItemSelected(MenuItem item) {
93 | int id = item.getItemId();
94 | Intent intent = null;
95 | switch (id)
96 | {
97 | case R.id.menu_item_remove_friend:
98 | if(mCurrentPosition > -1)
99 | {
100 | mFriendListAdapter.removeUserData(mCurrentPosition);
101 | }
102 | break;
103 | }
104 | return super.onContextItemSelected(item);
105 | }
106 |
107 |
108 |
109 | //创建上下文菜单时触发该方法。
110 | @Override
111 | public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
112 | super.onCreateContextMenu(menu, v, menuInfo);
113 | getMenuInflater().inflate(R.menu.friend_list_context_menu,menu);
114 | }
115 |
116 | //创建聊天对象管理监听器,监听从远端发送过来的message。
117 | private ChatManagerListener chatManagerListenerMain = new ChatManagerListener() {
118 | @Override
119 | public void chatCreated(Chat chat, boolean b) {
120 | chat.addMessageListener(new ChatMessageListener() {
121 | @Override
122 | public void processMessage(Chat chat, Message message) {
123 | android.os.Message msg = android.os.Message.obtain();
124 | msg.obj = message;
125 | myHandler.sendMessage(msg);
126 |
127 | }
128 | });
129 | }
130 | };
131 |
132 | //创建菜单
133 | @Override
134 | public boolean onCreateOptionsMenu(Menu menu) {
135 | super.onCreateOptionsMenu(menu);
136 | getMenuInflater().inflate(R.menu.menu_main,menu);
137 | return true;
138 | }
139 |
140 | //相应菜单
141 | @Override
142 | public boolean onOptionsItemSelected(MenuItem item) {
143 | int id = item.getItemId();
144 | Intent intent = null;
145 |
146 | switch (id)
147 | {
148 | //聊天界面
149 | case R.id.add_friend:
150 | intent = new Intent(this,AddFriendActivity.class);
151 | startActivityForResult(intent,1);
152 | break;
153 | //聊天室服务界面,显示聊天室服务。
154 | case R.id.chat_services:
155 | intent = new Intent(this,ChatServiceActivity.class);
156 | startActivity(intent);
157 | break;
158 | case R.id.menu_item_logout:
159 | AlertDialog.Builder builder = new AlertDialog.Builder(this)
160 | .setTitle("注销")
161 | .setMessage("确定要注销当前用户吗?")
162 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
163 | @Override
164 | public void onClick(DialogInterface dialog, int which) {
165 | Storage.putBollean(MainActivity.this, Const.KEY_AUTO_LOGIN, false);
166 | try {
167 | connection.disconnect();
168 | }
169 | catch (Exception e)
170 | {
171 | e.printStackTrace();
172 | }
173 | Intent intent = new Intent(MainActivity.this, LoginActivity.class);
174 | startActivity(intent);
175 | finish();
176 | }
177 | })
178 | .setNegativeButton("取消",null);
179 | builder.show();
180 | break;
181 | }
182 | return super.onOptionsItemSelected(item);
183 | }
184 |
185 | @Override
186 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
187 | super.onActivityResult(requestCode, resultCode, data);
188 | switch (requestCode)
189 | {
190 | case 1:
191 | if (resultCode == 1)
192 | {
193 | String username = data.getStringExtra("friendName");
194 |
195 | UserData userData = new UserData(username);
196 | //实时刷新ListView
197 | mFriendListAdapter.addUserData(userData);
198 | }
199 | }
200 | }
201 |
202 | private static class MyHandler extends Handler {
203 | private Context mContext;
204 | private MainActivity mMainActivity;
205 |
206 | public MyHandler(Context mContext) {
207 | this.mContext = mContext;
208 | this.mMainActivity = (MainActivity) mContext;
209 | }
210 |
211 | @Override
212 | public void handleMessage(android.os.Message msg) {
213 | super.handleMessage(msg);
214 | Message message = (Message) msg.obj;
215 | final String friendUsername = Util.extractUserFromChat(message.getFrom());
216 | //如果包含视频通话信息
217 | if (message.hasExtension(VideoInvitation.ELEMENT_NAME,VideoInvitation.NAME_SPACE))
218 | {
219 | // video-invitation
220 | // Log.e("XML","XML: "+message.getExtension(VideoInvitation.NAME_SPACE).toXML());
221 |
222 | DefaultExtensionElement defaultExtensionElement = message.getExtension(VideoInvitation.ELEMENT_NAME,
223 | VideoInvitation.NAME_SPACE);
224 | String type = defaultExtensionElement.getValue("type");
225 |
226 | switch (type)
227 | {
228 | case "video-invitation":
229 | AlertDialog.Builder builder = new AlertDialog.Builder(mMainActivity)
230 | .setTitle("视频聊天")
231 | .setMessage(friendUsername + "请求与你视频聊天");
232 |
233 | builder.setNegativeButton("拒绝", new DialogInterface.OnClickListener() {
234 | @Override
235 | public void onClick(DialogInterface dialog, int which) {
236 | //点击拒绝按钮需要做的事情。
237 | VideoInvitation videoInvitation = new VideoInvitation();
238 | videoInvitation.setTypeText("video-deny");
239 | String chatJid = friendUsername+"@"+mMainActivity.mServiceName;
240 | Message message = new Message();
241 | message.addExtension(videoInvitation);
242 |
243 | Chat chat = mMainActivity.createChat(chatJid);
244 | try {
245 | chat.sendMessage(message);
246 | } catch (SmackException.NotConnectedException e) {
247 | e.printStackTrace();
248 | }
249 | }
250 | });
251 |
252 | //被叫方跳转到VideoCallActivity
253 | builder.setPositiveButton("接受", new DialogInterface.OnClickListener() {
254 | @Override
255 | public void onClick(DialogInterface dialog, int which) {
256 | //单击接受视频通话按钮后,处理的动作
257 | VideoInvitation videoInvitation = new VideoInvitation();
258 | videoInvitation.setTypeText("video-agree");
259 | String chatJid = friendUsername+"@"+mMainActivity.mServiceName;
260 | Message message = new Message();
261 | message.addExtension(videoInvitation);
262 |
263 | Chat chat = mMainActivity.createChat(chatJid);
264 | try {
265 | chat.sendMessage(message);
266 | } catch (SmackException.NotConnectedException e) {
267 | e.printStackTrace();
268 | }
269 | Intent intent = new Intent(mMainActivity,VideoCallActivity.class);
270 | intent.putExtra("remoteName",friendUsername);
271 | mMainActivity.startActivity(intent);
272 | }
273 | });
274 |
275 | builder.create().show();
276 | break;
277 |
278 | case "video-deny":
279 | Toast.makeText(mMainActivity,"对方拒绝视频邀请",Toast.LENGTH_SHORT).show();
280 | break;
281 | //主叫方跳转到VideoCallActivity
282 | case "video-agree":
283 | Intent intent = new Intent(mMainActivity,VideoCallActivity.class);
284 | intent.putExtra("createOffer",true);
285 | Log.e("getFrom",friendUsername);
286 | intent.putExtra("remoteName",friendUsername);
287 | mMainActivity.startActivity(intent);
288 | break;
289 | //视频通话结束,则结束VideoCallActivity。
290 | case "video-ended":
291 | Intent intentEndVideo = new Intent(mMainActivity,VideoCallActivity.class);
292 | intentEndVideo.putExtra("videoEnded",true);
293 | mMainActivity.startActivity(intentEndVideo);
294 | break;
295 | }
296 | }
297 | //如果是SDP信息
298 | else if (message.hasExtension(SDPExtensionElement.ELEMENT_NAME,SDPExtensionElement.NAME_SPACE))
299 | {
300 | DefaultExtensionElement defaultExtensionElement =
301 | message.getExtension(SDPExtensionElement.ELEMENT_NAME, SDPExtensionElement.NAME_SPACE);
302 | String type = defaultExtensionElement.getValue("type");
303 | String description = defaultExtensionElement.getValue("description");
304 |
305 | //发送SDP数据到VideoCallActivity
306 | Intent intent = new Intent(mMainActivity,VideoCallActivity.class);
307 | intent.putExtra("type",type);
308 | intent.putExtra("description",description);
309 |
310 | mMainActivity.startActivity(intent);
311 | }
312 | //如果是ICE Candidate
313 | else if (message.hasExtension(IceCandidateExtensionElement.ELEMENT_NAME,
314 | IceCandidateExtensionElement.NAME_SPACE))
315 | {
316 | DefaultExtensionElement defaultExtensionElement =
317 | message.getExtension(IceCandidateExtensionElement.ELEMENT_NAME, IceCandidateExtensionElement.NAME_SPACE);
318 | String sdpMid = defaultExtensionElement.getValue("sdpMid");
319 | int sdpMLineIndex = Integer.parseInt(defaultExtensionElement.getValue("sdpMLineIndex"));
320 | String sdp = defaultExtensionElement.getValue("sdp");
321 | //发送ICE Candidate数据到VideoCallActivity
322 | Intent intent = new Intent(mMainActivity,VideoCallActivity.class);
323 | intent.putExtra("sdpMid",sdpMid);
324 | intent.putExtra("sdpMLineIndex",sdpMLineIndex);
325 | intent.putExtra("sdp",sdp);
326 |
327 | mMainActivity.startActivity(intent);
328 | }
329 | //如果是聊天文本信息
330 | else if (!mMainActivity.mChatUsers.contains(friendUsername))
331 | {
332 | Log.e("Handler","handler message");
333 | String body = message.getBody();
334 | if (body == null)
335 | {
336 | return;
337 | }
338 |
339 | mMainActivity.mChatUsers.add(friendUsername);
340 |
341 | Intent intent = new Intent(mMainActivity,ChatActivity.class);
342 | intent.putExtra("friendUsername", friendUsername);
343 | intent.putExtra("body",body);
344 | mMainActivity.startActivity(intent);
345 | }
346 | else
347 | {
348 | String body = message.getBody();
349 | if (body == null) {
350 | return;
351 | }
352 | Intent intent = new Intent(mMainActivity,ChatActivity.class);
353 | intent.putExtra("body",body);
354 | mMainActivity.startActivity(intent);
355 | }
356 | }
357 | }
358 | }
359 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/activity/MultiUserChatRoomActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.activity;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.graphics.Bitmap;
6 | import android.graphics.BitmapFactory;
7 | import android.graphics.Matrix;
8 | import android.os.Handler;
9 | import android.support.v7.app.AppCompatActivity;
10 | import android.os.Bundle;
11 | import android.text.SpannableString;
12 | import android.text.Spanned;
13 | import android.text.style.ImageSpan;
14 | import android.util.Log;
15 | import android.view.View;
16 | import android.widget.EditText;
17 | import android.widget.ListView;
18 | import android.widget.Toast;
19 |
20 | import com.example.adapter.ChatListAdapter;
21 | import com.example.common.Const;
22 | import com.example.common.Util;
23 | import com.example.data.ChatData;
24 | import com.example.data.DataWarehouse;
25 | import com.example.data.LoginData;
26 |
27 | import org.jivesoftware.smack.MessageListener;
28 | import org.jivesoftware.smack.chat.ChatManager;
29 | import org.jivesoftware.smack.filter.MessageTypeFilter;
30 | import org.jivesoftware.smack.filter.PacketFilter;
31 | import org.jivesoftware.smack.packet.Message;
32 | import org.jivesoftware.smackx.muc.MultiUserChat;
33 | import org.jivesoftware.smackx.muc.MultiUserChatManager;
34 |
35 | public class MultiUserChatRoomActivity extends ParentActivity implements Const{
36 | private ChatListAdapter mChatListAdapter;
37 |
38 | private ChatManager mChatManager;
39 | private LoginData mLoginData;
40 |
41 | private EditText mEditTextChatText;
42 | private ListView mListViewChatList;
43 | private String mChatServiceJID;
44 | private String mChatRoomName;
45 | private MultiUserChat mMultiUserChat;
46 |
47 | // private PacketFilter mFilter = new MessageTypeFilter(Message.Type.groupchat);
48 |
49 | @Override
50 | protected void onCreate(Bundle savedInstanceState) {
51 | super.onCreate(savedInstanceState);
52 | setContentView(R.layout.activity_multi_user_chat_room);
53 |
54 | mLoginData = DataWarehouse.getGlobalData(this).loginData;
55 |
56 | mEditTextChatText = (EditText) findViewById(R.id.edittext_chat_text);
57 | mListViewChatList = (ListView) findViewById(R.id.listview_ChatList);
58 |
59 | // mXMPPConnection.addPacketListener(this, mFilter);
60 | mChatServiceJID = getIntent().getStringExtra(JID);
61 | mChatRoomName = getIntent().getStringExtra(CHAT_ROOM_NAME);
62 |
63 | mChatListAdapter = new ChatListAdapter(this);
64 |
65 | mListViewChatList.setAdapter(mChatListAdapter);
66 | mListViewChatList.setDivider(null);
67 |
68 | //加入聊天室
69 | try {
70 | mMultiUserChat = MultiUserChatManager.getInstanceFor(connection).
71 | getMultiUserChat(mChatRoomName+"@"+mChatServiceJID);
72 | mMultiUserChat.join(mLoginData.userName);
73 | }
74 | catch (Exception e)
75 | {
76 | e.printStackTrace();
77 | Toast.makeText(this, "聊天室加入失败", Toast.LENGTH_LONG).show();
78 | finish();
79 | }
80 |
81 | // myHandler = new MyHandler(this);
82 |
83 | //接收群聊信息
84 | mMultiUserChat.addMessageListener(new MessageListener() {
85 | @Override
86 | public void processMessage(Message message) {
87 | Log.e("ChatGroup",message.getFrom());
88 | Log.e("MsgBody",message.getBody());
89 | android.os.Message msg = android.os.Message.obtain();
90 | msg.obj = message;
91 | // myHandler.sendMessage(msg);
92 | handler.sendMessage(msg);
93 | }
94 | });
95 | }
96 |
97 | private Handler handler = new Handler(){
98 | @Override
99 | public void handleMessage(android.os.Message msg) {
100 | super.handleMessage(msg);
101 | Message message = (Message) msg.obj;
102 | String body = message.getBody();
103 | if (body != null)
104 | {
105 | String from = Util.extractUserFromChatGroup(message.getFrom());
106 | if(mLoginData.userName.equals(from))
107 | {
108 | return;
109 | }
110 |
111 | ChatData item = new ChatData();
112 |
113 | item.text = body;
114 |
115 | mChatListAdapter.addItem(item);
116 | mListViewChatList.setSelection(mListViewChatList.getAdapter().getCount() - 1);
117 | }
118 | return;
119 | }
120 | };
121 |
122 |
123 |
124 | @Override
125 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
126 | switch (resultCode)
127 | {
128 | case 1:
129 | int faceId = data.getIntExtra(KEY_FACE_ID, -1);
130 | if (faceId != -1)
131 | {
132 | String faceResName = FACE_PREFIX + faceId;
133 |
134 | Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
135 | Util.getResourceIDFromName(R.drawable.class, faceResName));
136 |
137 | Matrix matrix = new Matrix();
138 | matrix.postScale(0.6f, 0.6f);
139 | Bitmap smallBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),
140 | matrix, true);
141 | ImageSpan imageSpan = new ImageSpan(this, smallBitmap);
142 | String faceText = FACE_TEXT_PREFIX + faceId + FACE_TEXT_SUFFIX;
143 | SpannableString spannableString = new SpannableString(faceText);
144 |
145 | spannableString.setSpan(imageSpan, 0, faceText.length(),
146 | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
147 |
148 | mEditTextChatText.getText().insert(mEditTextChatText.getSelectionStart(),
149 | spannableString);
150 | }
151 | break;
152 | }
153 | }
154 |
155 | //发送群聊信息
156 | public void onClick_Send(View view) {
157 | try
158 | {
159 | String text = mEditTextChatText.getText().toString().trim();
160 | if (!"".equals(text))
161 | {
162 | mMultiUserChat.sendMessage(text);
163 | mEditTextChatText.setText("");
164 |
165 | ChatData item = new ChatData();
166 | item.text = text;
167 | item.user = mLoginData.userName;
168 | item.isOwner = true;
169 | mChatListAdapter.addItem(item);
170 | mListViewChatList.setSelection(mListViewChatList.getAdapter().getCount() - 1);
171 | }
172 | else
173 | {
174 | Toast.makeText(this, "请输入要发送的文本.", Toast.LENGTH_LONG).show();
175 | }
176 | }
177 | catch (Exception e)
178 | {
179 | e.printStackTrace();
180 | Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
181 | }
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/activity/MultiUserChatRoomsActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.activity;
2 |
3 | import android.content.DialogInterface;
4 | import android.content.Intent;
5 | import android.support.v7.app.AlertDialog;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.os.Bundle;
8 | import android.view.Menu;
9 | import android.view.MenuItem;
10 | import android.view.View;
11 | import android.widget.AdapterView;
12 | import android.widget.EditText;
13 | import android.widget.ListView;
14 | import android.widget.Toast;
15 |
16 | import com.example.adapter.MultiUserChatRoomListAdapter;
17 | import com.example.common.Const;
18 | import com.example.data.DataWarehouse;
19 |
20 | import org.jivesoftware.smack.SmackException;
21 | import org.jivesoftware.smack.XMPPException;
22 | import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
23 | import org.jivesoftware.smackx.disco.packet.DiscoverItems;
24 | import org.jivesoftware.smackx.muc.MultiUserChat;
25 | import org.jivesoftware.smackx.muc.MultiUserChatManager;
26 | import org.jivesoftware.smackx.xdata.Form;
27 | import org.jivesoftware.smackx.xdata.FormField;
28 |
29 | import java.util.List;
30 |
31 | public class MultiUserChatRoomsActivity extends ParentActivity implements Const {
32 | private ListView listView;
33 | private MultiUserChatRoomListAdapter multiUserChatRoomListAdapter;
34 | private ServiceDiscoveryManager serviceDiscoveryManager;
35 | private String chatServiceJID;
36 |
37 | @Override
38 | protected void onCreate(Bundle savedInstanceState) {
39 | super.onCreate(savedInstanceState);
40 | setContentView(R.layout.activity_multi_user_chat_rooms);
41 |
42 | listView = (ListView) findViewById(R.id.listview_chat_rooms);
43 | chatServiceJID = getIntent().getStringExtra(JID);
44 | setTitle(chatServiceJID);
45 | serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
46 |
47 | try {
48 | multiUserChatRoomListAdapter = new MultiUserChatRoomListAdapter(this,
49 | serviceDiscoveryManager.discoverItems(chatServiceJID).getItems());
50 | } catch (SmackException.NoResponseException e) {
51 | e.printStackTrace();
52 | } catch (XMPPException.XMPPErrorException e) {
53 | e.printStackTrace();
54 | } catch (SmackException.NotConnectedException e) {
55 | e.printStackTrace();
56 | }
57 |
58 | listView.setAdapter(multiUserChatRoomListAdapter);
59 |
60 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
61 | @Override
62 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
63 | DiscoverItems.Item item = multiUserChatRoomListAdapter.getChatRoom(position);
64 | Intent intent = new Intent(MultiUserChatRoomsActivity.this, MultiUserChatRoomActivity.class);
65 | intent.putExtra(JID, chatServiceJID);
66 | intent.putExtra(CHAT_ROOM_NAME, item.getName());
67 | startActivity(intent);
68 |
69 | }
70 | });
71 | }
72 |
73 | //新建聊天室
74 | public void onClick_New_Room(View view) {
75 | final EditText editText = new EditText(MultiUserChatRoomsActivity.this);
76 | AlertDialog.Builder builder = new AlertDialog.Builder(MultiUserChatRoomsActivity.this)
77 | .setTitle("请输入聊天室名称")
78 | .setView(editText)
79 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
80 | @Override
81 | public void onClick(DialogInterface dialog, int which) {
82 | String chatRoomName = editText.getText().toString().trim();
83 | if ("".equals(chatRoomName))
84 | {
85 | Toast.makeText(MultiUserChatRoomsActivity.this,
86 | "聊天时名称不能为空",Toast.LENGTH_LONG).show();
87 | return;
88 | }
89 | if (!isConnected())
90 | {
91 | throw new NullPointerException("服务器连接失败,请先连接服务器");
92 | }
93 |
94 | // 创建一个MultiUserChat
95 | MultiUserChat muc = null;
96 | muc = MultiUserChatManager.getInstanceFor(connection).getMultiUserChat(
97 | chatRoomName+"@"+chatServiceJID);
98 | // 创建聊天室
99 | try {
100 | boolean isCreate = muc.createOrJoin(DataWarehouse.getGlobalData(
101 | MultiUserChatRoomsActivity.this).loginData.userName);
102 | } catch (XMPPException.XMPPErrorException e) {
103 | e.printStackTrace();
104 | } catch (SmackException e) {
105 | e.printStackTrace();
106 | }
107 | Form form = null;
108 | try {
109 | form = muc.getConfigurationForm();
110 | } catch (SmackException.NoResponseException e) {
111 | e.printStackTrace();
112 | } catch (XMPPException.XMPPErrorException e) {
113 | e.printStackTrace();
114 | } catch (SmackException.NotConnectedException e) {
115 | e.printStackTrace();
116 | }
117 | Form submitForm = form.createAnswerForm();
118 | List fields = form.getFields();
119 | for (FormField field : fields) {
120 | if (!FormField.Type.hidden.equals(field.getType()) &&
121 | field.getVariable() != null) {
122 | submitForm.setDefaultAnswer(field.getVariable());
123 | }
124 | }
125 |
126 | // 设置聊天室为公共聊天室
127 | submitForm.setAnswer("muc#roomconfig_publicroom", true);
128 | // 设置聊天室是永久存在的
129 | submitForm.setAnswer("muc#roomconfig_persistentroom", true);
130 |
131 | try {
132 | muc.sendConfigurationForm(submitForm);
133 | } catch (SmackException.NoResponseException e) {
134 | e.printStackTrace();
135 | } catch (XMPPException.XMPPErrorException e) {
136 | e.printStackTrace();
137 | } catch (SmackException.NotConnectedException e) {
138 | e.printStackTrace();
139 | }
140 | try {
141 | multiUserChatRoomListAdapter.updateChatRoom(serviceDiscoveryManager.
142 | discoverItems(chatServiceJID).getItems());
143 | } catch (SmackException.NoResponseException e) {
144 | e.printStackTrace();
145 | } catch (XMPPException.XMPPErrorException e) {
146 | e.printStackTrace();
147 | } catch (SmackException.NotConnectedException e) {
148 | e.printStackTrace();
149 | }
150 | Toast.makeText(MultiUserChatRoomsActivity.this, "成功创建聊天室", Toast.LENGTH_LONG).show();
151 |
152 | }
153 | })
154 | .setNegativeButton("取消",null);
155 | builder.create().show();
156 | }
157 |
158 | @Override
159 | public boolean onCreateOptionsMenu(Menu menu) {
160 | // Inflate the menu; this adds items to the action bar if it is present.
161 | getMenuInflater().inflate(R.menu.menu_chat_room, menu);
162 | return true;
163 | }
164 |
165 | @Override
166 | public boolean onOptionsItemSelected(MenuItem item) {
167 | int id = item.getItemId();
168 | switch(id)
169 | {
170 | case R.id.menu_item_chat_room_refresh:
171 | try
172 | {
173 | multiUserChatRoomListAdapter.updateChatRoom(serviceDiscoveryManager.
174 | discoverItems(chatServiceJID).getItems());
175 | Toast.makeText(this, "成功刷新", Toast.LENGTH_LONG).show();
176 | }
177 | catch (Exception e)
178 | {
179 | Toast.makeText(this, "刷新失败", Toast.LENGTH_LONG).show();
180 | }
181 | break;
182 | }
183 |
184 | return super.onOptionsItemSelected(item);
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/activity/ParentActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.activity;
2 | /*创建ParentActivity的原因:是因为好多Activity都需要用到下面的connetiton
3 | *对象,故将需要connection的Activity继承ParentActivity对象即可。
4 | */
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.os.Bundle;
7 |
8 | import com.example.data.DataWarehouse;
9 |
10 | import org.jivesoftware.smack.SmackException;
11 | import org.jivesoftware.smack.XMPPException;
12 | import org.jivesoftware.smack.chat.Chat;
13 | import org.jivesoftware.smack.chat.ChatManager;
14 | import org.jivesoftware.smack.tcp.XMPPTCPConnection;
15 |
16 | import java.io.IOException;
17 |
18 | public class ParentActivity extends AppCompatActivity {
19 | protected XMPPTCPConnection connection;
20 |
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | connection = DataWarehouse.getXMPPTCPConnection(this);
25 | }
26 |
27 | protected boolean isConnected()
28 | {
29 | if (connection == null)
30 | {
31 | return false;
32 | }
33 | if (!connection.isConnected())
34 | {
35 | try {
36 | connection.connect();
37 | return true;
38 | } catch (SmackException e) {
39 | e.printStackTrace();
40 | } catch (IOException e) {
41 | e.printStackTrace();
42 | } catch (XMPPException e) {
43 | e.printStackTrace();
44 | }
45 | }
46 | return true;
47 | }
48 |
49 | //获取聊天对象管理器
50 | public ChatManager getChatManager()
51 | {
52 | if (isConnected())
53 | {
54 | ChatManager chatManager = ChatManager.getInstanceFor(connection);
55 | return chatManager;
56 | }
57 | throw new NullPointerException("服务器连接失败,请先连接服务器!");
58 | }
59 |
60 | //chatJid: friendUsername@serviceName
61 | protected Chat createChat(String chatJid)
62 | {
63 | if (isConnected())
64 | {
65 | ChatManager chatManager = ChatManager.getInstanceFor(connection);
66 | return chatManager.createChat(chatJid);
67 | }
68 | throw new NullPointerException("连接服务器失败,请先连接服务器!");
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/activity/RegisterActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.activity;
2 |
3 | import android.support.v7.app.AppCompatActivity;
4 | import android.os.Bundle;
5 | import android.util.Log;
6 | import android.view.View;
7 | import android.widget.Button;
8 | import android.widget.EditText;
9 | import android.widget.Toast;
10 |
11 | import com.example.common.XMPPUtil;
12 |
13 | import org.jivesoftware.smack.SmackException;
14 | import org.jivesoftware.smack.XMPPConnection;
15 | import org.jivesoftware.smack.XMPPException;
16 | import org.jivesoftware.smack.packet.IQ;
17 | import org.jivesoftware.smack.tcp.XMPPTCPConnection;
18 | import org.jivesoftware.smackx.iqregister.AccountManager;
19 | import org.jivesoftware.smackx.iqregister.packet.Registration;
20 |
21 | import java.io.IOException;
22 |
23 | public class RegisterActivity extends AppCompatActivity {
24 | private EditText mEditTextUsername;
25 | private EditText mEditTextPassword;
26 |
27 | @Override
28 | protected void onCreate(Bundle savedInstanceState) {
29 | super.onCreate(savedInstanceState);
30 | setContentView(R.layout.activity_register);
31 |
32 | mEditTextUsername = (EditText) findViewById(R.id.userRegister);
33 | mEditTextPassword = (EditText) findViewById(R.id.passwordInput);
34 | }
35 |
36 |
37 | public void onClickRegister(View view) {
38 | new Thread(new Runnable() {
39 | @Override
40 | public void run() {
41 | registerUser(mEditTextUsername.getText().toString(),mEditTextPassword.getText().toString());
42 | }
43 | }).start();
44 | }
45 |
46 |
47 | //注册用户
48 | public Boolean registerUser(String username,String password)
49 | {
50 | try {
51 | XMPPTCPConnection connection = XMPPUtil.getXMPPConnection(this);
52 | if (connection == null)
53 | {
54 | try {
55 | connection.connect();
56 | } catch (SmackException e) {
57 | e.printStackTrace();
58 | } catch (IOException e) {
59 | e.printStackTrace();
60 | } catch (XMPPException e) {
61 | e.printStackTrace();
62 | }
63 | Log.e("connect","连接服务器失败!");
64 | }
65 |
66 | AccountManager.getInstance(connection).createAccount(username,password);
67 | finish();
68 | return true;
69 | }
70 | catch (SmackException.NoResponseException | XMPPException.XMPPErrorException |
71 | SmackException.NotConnectedException e)
72 | {
73 | Log.e("register", "注册失败!");
74 | return false;
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/activity/VideoCallActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.activity;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.media.AudioManager;
6 | import android.opengl.GLSurfaceView;
7 | import android.os.Bundle;
8 | import android.util.DisplayMetrics;
9 | import android.util.Log;
10 | import android.view.WindowManager;
11 |
12 | import com.example.CustomXML.IceCandidateExtensionElement;
13 | import com.example.CustomXML.SDPExtensionElement;
14 | import com.example.CustomXML.VideoInvitation;
15 |
16 | import org.jivesoftware.smack.SmackException;
17 | import org.jivesoftware.smack.chat.Chat;
18 | import org.jivesoftware.smack.packet.Message;
19 | import org.webrtc.AudioSource;
20 | import org.webrtc.AudioTrack;
21 | import org.webrtc.DataChannel;
22 | import org.webrtc.IceCandidate;
23 | import org.webrtc.MediaConstraints;
24 | import org.webrtc.MediaStream;
25 | import org.webrtc.PeerConnection;
26 | import org.webrtc.PeerConnectionFactory;
27 | import org.webrtc.SdpObserver;
28 | import org.webrtc.SessionDescription;
29 | import org.webrtc.VideoCapturer;
30 | import org.webrtc.VideoCapturerAndroid;
31 | import org.webrtc.VideoRenderer;
32 | import org.webrtc.VideoRendererGui;
33 | import org.webrtc.VideoSource;
34 | import org.webrtc.VideoTrack;
35 |
36 | import java.util.ArrayList;
37 | import java.util.LinkedList;
38 | import java.util.List;
39 |
40 | public class VideoCallActivity extends ParentActivity{
41 | public static final String VIDEO_TRACK_ID = "ARDAMSv0";
42 | public static final String AUDIO_TRACK_ID = "ARDAMSa0";
43 | public static final String LOCAL_MEDIA_STREAM_ID = "ARDAMS";
44 | private String mServiceName; //XMPP服务器名称
45 |
46 | private GLSurfaceView mGLSurfaceView;
47 | // private GLSurfaceView mGLSurfaceViewRemote;
48 |
49 | private PeerConnection pc;
50 | private final PCObserver pcObserver = new PCObserver();
51 | private final SDPObserver sdpObserver = new SDPObserver();
52 |
53 | private MediaConstraints sdpMediaConstraints;
54 | private MediaConstraints pcConstraints;
55 | private String remoteName;
56 | IceCandidate remoteIceCandidate;
57 |
58 | private boolean mIsInited;
59 | private boolean mIsCalled;
60 | PeerConnectionFactory factory;
61 | VideoCapturer videoCapturer;
62 | VideoSource videoSource;
63 |
64 | VideoRenderer localVideoRenderer;
65 | VideoRenderer remoteVideoRenderer;
66 |
67 | AudioManager audioManager;
68 |
69 | @Override
70 | protected void onCreate(Bundle savedInstanceState) {
71 | super.onCreate(savedInstanceState);
72 | setContentView(R.layout.activity_video_call);
73 |
74 | //打开扬声器
75 | audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
76 | audioManager.setSpeakerphoneOn(true);
77 |
78 | mServiceName = connection.getServiceName();
79 | mGLSurfaceView = (GLSurfaceView) findViewById(R.id.glsurfaceview);
80 | // mGLSurfaceViewRemote = (GLSurfaceView) findViewById(R.id.glsurfaceview_remote);
81 | //检查初始化音视频设备是否成功
82 | if (!PeerConnectionFactory.initializeAndroidGlobals(this,true,true,true,null))
83 | {
84 | Log.e("init","PeerConnectionFactory init fail!");
85 | return;
86 | }
87 |
88 | // Intent intent = getIntent().getBundleExtra()
89 |
90 | //Media条件信息SDP接口
91 | sdpMediaConstraints = new MediaConstraints();
92 | //接受远程音频
93 | sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
94 | "OfferToReceiveAudio", "true"));
95 | //接受远程视频
96 | sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair(
97 | "OfferToReceiveVideo", "true"));
98 | // sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googEchoCancellation","true"));
99 | // sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googAutoGainControl","true"));
100 | // sdpMediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googNoiseReduction","true"));
101 |
102 | factory = new PeerConnectionFactory();
103 |
104 | //iceServer List对象获取
105 | List iceServers = new ArrayList<>();
106 | pcConstraints = new MediaConstraints();
107 | pcConstraints.optional.add(new MediaConstraints.KeyValuePair(
108 | "DtlsSrtpKeyAgreement", "true"));
109 | pcConstraints.mandatory.add(new
110 | MediaConstraints.KeyValuePair("VoiceActivityDetection", "false"));
111 | // pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googEchoCancellation","true"));
112 | // pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googAutoGainControl","true"));
113 | // pcConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googNoiseReduction","true"));
114 |
115 | pc = factory.createPeerConnection(iceServers,pcConstraints,pcObserver);
116 |
117 |
118 | mIsInited = false;
119 | mIsCalled=false;
120 |
121 | boolean offer=getIntent().getBooleanExtra("createOffer",false);
122 | //offer:如果offer为true表示主叫方初始化,如果为false表示被叫方初始化。
123 | remoteName = getIntent().getStringExtra("remoteName");
124 | if(!offer)
125 | {
126 | initialSystem();
127 | }
128 | else {
129 | callRemote(remoteName);
130 | }
131 | //当VideoActivity已经打开时,处理后续的intent传过来的数据。
132 | processExtraData();
133 | }
134 |
135 | @Override
136 | protected void onNewIntent(Intent intent) {
137 | super.onNewIntent(intent);
138 | setIntent(intent);
139 | processExtraData();
140 | }
141 |
142 | @Override
143 | protected void onDestroy() {
144 | super.onDestroy();
145 | //通话结束,发送通话结束消息
146 | videoCallEnded();
147 | //释放资源
148 | videoCapturer.dispose();
149 | videoSource.stop();
150 | if (pc != null) {
151 | pc.dispose();
152 | pc = null;
153 | }
154 |
155 | audioManager.setSpeakerphoneOn(false);
156 | }
157 |
158 | private void videoCallEnded() {
159 | String chatJid = remoteName+"@"+mServiceName;
160 | Message message = new Message();
161 | VideoInvitation videoInvitation = new VideoInvitation();
162 | videoInvitation.setTypeText("video-ended");
163 | message.addExtension(videoInvitation);
164 | Chat chat = createChat(chatJid);
165 | try {
166 | chat.sendMessage(message);
167 | } catch (SmackException.NotConnectedException e) {
168 | e.printStackTrace();
169 | }
170 | }
171 |
172 | private void processExtraData() {
173 | Intent intent = getIntent();
174 | //获取SDP数据
175 | String sdpType = intent.getStringExtra("type");
176 | String sdpDescription = intent.getStringExtra("description");
177 | if (sdpType != null)
178 | {
179 |
180 | SessionDescription.Type type = SessionDescription.Type.fromCanonicalForm(sdpType);
181 | SessionDescription sdp = new SessionDescription(type,sdpDescription);
182 | if (pc == null)
183 | {
184 | Log.e("pc","pc == null");
185 | }
186 | pc.setRemoteDescription(sdpObserver,sdp);
187 |
188 | //如果是offer,则被叫方createAnswer
189 | if (sdpType.equals("offer"))
190 | {
191 | mIsCalled = true;
192 | pc.createAnswer(sdpObserver,sdpMediaConstraints);
193 | }
194 |
195 | }
196 |
197 | //获取ICE Candidate数据
198 | String iceSdpMid = intent.getStringExtra("sdpMid");
199 | int iceSdpMLineIndex = intent.getIntExtra("sdpMLineIndex",-1);
200 | String iceSdp = intent.getStringExtra("sdp");
201 | if (iceSdpMid != null)
202 | {
203 | IceCandidate iceCandidate = new IceCandidate(iceSdpMid,iceSdpMLineIndex,iceSdp);
204 |
205 | if (remoteIceCandidate == null)
206 | {
207 | remoteIceCandidate = iceCandidate;
208 | }
209 |
210 | //下面这步放到函数drainRemoteCandidates()中
211 | /*//添加远端的IceCandidate到pc
212 | pc.addIceCandidate(iceCandidate);*/
213 | }
214 |
215 |
216 | //结束activity
217 | boolean videoEnded = intent.getBooleanExtra("videoEnded",false);
218 | if (videoEnded)
219 | {
220 | finish();
221 | }
222 | }
223 |
224 | /* //chatJid: friendUsername@serviceName
225 | private Chat createChat(String chatJid)
226 | {
227 | if (isConnected())
228 | {
229 | ChatManager chatManager = ChatManager.getInstanceFor(connection);
230 | return chatManager.createChat(chatJid);
231 | }
232 | throw new NullPointerException("连接服务器失败,请先连接服务器!");
233 | }*/
234 |
235 | private void callRemote(String remoteName) {
236 | initialSystem();
237 | //createOffer
238 | pc.createOffer(sdpObserver,sdpMediaConstraints);
239 | }
240 |
241 | private void initialSystem() {
242 | if (mIsInited)
243 | {
244 | return;
245 | }
246 | //获取前置摄像头本地视频流
247 | String frontDeviceName = VideoCapturerAndroid.getNameOfFrontFacingDevice();
248 | // String frontDeviceName = "Camera 1, Facing front, Orientation 0";
249 | Log.e("CameraName","CameraName: "+frontDeviceName);
250 | videoCapturer = VideoCapturerAndroid.create(frontDeviceName);
251 |
252 | /* //获取后置摄像头本地视频流
253 | String backDeviceName = VideoCapturerAndroid.getNameOfBackFacingDevice();
254 | Log.e("CameraName","CameraName: "+backDeviceName);
255 | videoCapturer = VideoCapturerAndroid.create(backDeviceName);*/
256 |
257 | /*int cameraNums = VideoCapturerAndroid.getDeviceCount();
258 | Log.e("CameraCount","CameraCount: "+cameraNums);*/
259 |
260 | /* //手机上直接使用上面注释代码指定前置和后置摄像头就行,当外接USB摄像头时,
261 | //使用下面代码获取USB摄像头。
262 | String[] cameraNames = VideoCapturerAndroid.getDeviceNames();
263 | for (String cameraName : cameraNames)
264 | {
265 | videoCapturer = VideoCapturerAndroid.create(cameraName);
266 | if (videoCapturer != null)
267 | {
268 | break;
269 | }
270 | }*/
271 |
272 | if (videoCapturer == null)
273 | {
274 | Log.e("open","fail to open camera");
275 | return;
276 | }
277 | //视频
278 | MediaConstraints mediaConstraints = new MediaConstraints();
279 | videoSource = factory.createVideoSource(videoCapturer, mediaConstraints);
280 | VideoTrack localVideoTrack = factory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
281 |
282 | //音频
283 | MediaConstraints audioConstraints = new MediaConstraints();
284 | AudioSource audioSource = factory.createAudioSource(audioConstraints);
285 | AudioTrack localAudioTrack = factory.createAudioTrack(AUDIO_TRACK_ID,audioSource);
286 |
287 | Runnable runnable = new Runnable() {
288 | @Override
289 | public void run() {
290 |
291 | }
292 | };
293 | VideoRendererGui.setView(mGLSurfaceView,runnable);
294 |
295 | WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
296 | DisplayMetrics dm = new DisplayMetrics();
297 | wm.getDefaultDisplay().getMetrics(dm);
298 | int width = dm.widthPixels; // 屏幕宽度(像素)
299 | int height = dm.heightPixels; // 屏幕高度(像素)
300 |
301 | try {
302 | //改成ScalingType.SCALE_ASPECT_FILL可以显示双方视频,但是显示比例不美观,并且不知道最后一个参数true和false的含义。
303 | localVideoRenderer = VideoRendererGui.createGui(0,0,100,100, VideoRendererGui.ScalingType.SCALE_ASPECT_FILL,true);
304 | remoteVideoRenderer = VideoRendererGui.createGui(0,0,100,100, VideoRendererGui.ScalingType.SCALE_ASPECT_FILL,true);
305 | localVideoTrack.addRenderer(localVideoRenderer);
306 | } catch (Exception e) {
307 | e.printStackTrace();
308 | }
309 |
310 | MediaStream localMediaStream = factory.createLocalMediaStream(LOCAL_MEDIA_STREAM_ID);
311 |
312 | localMediaStream.addTrack(localAudioTrack);
313 | localMediaStream.addTrack(localVideoTrack);
314 |
315 | pc.addStream(localMediaStream);
316 | }
317 |
318 | private List getIceServers(String url,String user,String credential)
319 | {
320 | PeerConnection.IceServer turn = new PeerConnection.IceServer(
321 | url,user,credential);
322 | LinkedList iceServers = new LinkedList();
323 | iceServers.add(turn);
324 | return iceServers;
325 | }
326 |
327 | private class PCObserver implements PeerConnection.Observer
328 | {
329 |
330 | @Override
331 | public void onSignalingChange(PeerConnection.SignalingState signalingState) {
332 |
333 | }
334 |
335 | @Override
336 | public void onIceConnectionChange(PeerConnection.IceConnectionState iceConnectionState) {
337 |
338 | }
339 |
340 | @Override
341 | public void onIceGatheringChange(PeerConnection.IceGatheringState iceGatheringState) {
342 |
343 | }
344 |
345 | //发送ICE候选到其他客户端
346 | @Override
347 | public void onIceCandidate(final IceCandidate iceCandidate) {
348 | //利用XMPP发送iceCandidate到其他客户端
349 | runOnUiThread(new Runnable() {
350 | @Override
351 | public void run() {
352 | String chatJid = remoteName+"@"+mServiceName;
353 | Message message = new Message();
354 | IceCandidateExtensionElement iceCandidateExtensionElement=
355 | new IceCandidateExtensionElement();
356 | iceCandidateExtensionElement.setSdpMidText(iceCandidate.sdpMid);
357 | iceCandidateExtensionElement.setSdpMLineIndexText(iceCandidate.sdpMLineIndex);
358 | iceCandidateExtensionElement.setSdpText(iceCandidate.sdp);
359 | message.addExtension(iceCandidateExtensionElement);
360 | Chat chat = createChat(chatJid);
361 | try {
362 | chat.sendMessage(message);
363 | } catch (SmackException.NotConnectedException e) {
364 | e.printStackTrace();
365 | }
366 |
367 | }
368 | });
369 | }
370 |
371 | //Display a media stream from remote
372 | @Override
373 | public void onAddStream(final MediaStream mediaStream) {
374 | runOnUiThread(new Runnable() {
375 | @Override
376 | public void run() {
377 | if (pc == null)
378 | {
379 | Log.e("onAddStream","pc == null");
380 | return;
381 | }
382 | if (mediaStream.videoTracks.size()>1 || mediaStream.audioTracks.size()>1)
383 | {
384 | Log.e("onAddStream","size > 1");
385 | return;
386 | }
387 | if (mediaStream.videoTracks.size() == 1)
388 | {
389 | /* Log.e("addStream","onAddStream() onStart");
390 | Log.e("mediaStream","mediaStream: "+mediaStream.toString());
391 | Log.e("streamSize","streamSize: "+mediaStream.videoTracks.size());*/
392 | VideoTrack videoTrack = mediaStream.videoTracks.get(0);
393 | videoTrack.addRenderer(remoteVideoRenderer);
394 | }
395 | }
396 | });
397 | }
398 |
399 | @Override
400 | public void onRemoveStream(final MediaStream mediaStream) {
401 | runOnUiThread(new Runnable() {
402 | @Override
403 | public void run() {
404 | mediaStream.videoTracks.get(0).dispose();
405 | }
406 | });
407 | }
408 |
409 | @Override
410 | public void onDataChannel(DataChannel dataChannel) {
411 |
412 | }
413 |
414 | @Override
415 | public void onRenegotiationNeeded() {
416 |
417 | }
418 | }
419 |
420 | private class SDPObserver implements SdpObserver
421 | {
422 |
423 | @Override
424 | public void onCreateSuccess(final SessionDescription sessionDescription) {
425 | //sendMessage(offer);
426 | runOnUiThread(new Runnable() {
427 | @Override
428 | public void run() {
429 | String chatJid = remoteName+"@"+mServiceName;
430 | Message message = new Message();
431 | SDPExtensionElement sdpExtensionElement = new SDPExtensionElement();
432 | sdpExtensionElement.setTypeText(sessionDescription.type.canonicalForm());
433 | sdpExtensionElement.setDescriptionText(sessionDescription.description);
434 | message.addExtension(sdpExtensionElement);
435 | Chat chat = createChat(chatJid);
436 | try {
437 | chat.sendMessage(message);
438 | } catch (SmackException.NotConnectedException e) {
439 | e.printStackTrace();
440 | }
441 |
442 | pc.setLocalDescription(sdpObserver,sessionDescription);
443 | }
444 | });
445 | }
446 |
447 | @Override
448 | public void onSetSuccess() {
449 | runOnUiThread(new Runnable() {
450 | @Override
451 | public void run() {
452 | //主叫方
453 | if (!mIsCalled)
454 | {
455 | if (pc.getRemoteDescription() != null)
456 | {
457 | drainRemoteCandidates();
458 | }
459 | }
460 | //被叫方
461 | else
462 | {
463 | //如果被叫方还没有createAnswer
464 | if (pc.getLocalDescription() == null)
465 | {
466 | Log.e("SDPObserver", "SDPObserver create answer");
467 | }
468 | else
469 | {
470 | drainRemoteCandidates();
471 | }
472 | }
473 | }
474 | });
475 | }
476 |
477 | private void drainRemoteCandidates() {
478 | if (remoteIceCandidate == null)
479 | {
480 | Log.e("SDPObserver","remoteIceCandidate == null");
481 | return;
482 | }
483 | pc.addIceCandidate(remoteIceCandidate);
484 | Log.e("IceCanditate","添加IceCandidate成功");
485 | remoteIceCandidate = null;
486 | }
487 |
488 | @Override
489 | public void onCreateFailure(String s) {
490 | Log.e("SDPObserver","onCreateFailure");
491 | }
492 |
493 | @Override
494 | public void onSetFailure(String s) {
495 | Log.e("SDPObserver","onSetFailure");
496 | }
497 | }
498 | }
499 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/adapter/ChatListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.example.adapter;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.BaseAdapter;
8 | import android.widget.ImageView;
9 | import android.widget.TextView;
10 |
11 | import com.example.activity.R;
12 | import com.example.common.Util;
13 | import com.example.data.ChatData;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 |
19 | public class ChatListAdapter extends BaseAdapter {
20 | private Context mContext;
21 | private List mChatDataList;
22 | private LayoutInflater mLayoutInflater;
23 |
24 | public ChatListAdapter(Context mContext) {
25 | this.mContext = mContext;
26 | mChatDataList = new ArrayList<>();
27 | mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
28 | }
29 |
30 | @Override
31 | public int getCount() {
32 | return mChatDataList.size();
33 | }
34 |
35 | @Override
36 | public Object getItem(int position) {
37 | return position mChatServiceList;
19 |
20 | public ChatServiceListAdapter(Context mContext, List chatServiceList) {
21 | super(mContext);
22 | mChatServiceList = new ArrayList<>();
23 | for (int i=0; i mUsers;
37 | private Map mUserMap;
38 | private Context mContext;
39 | private LayoutInflater mLayoutInflater;
40 |
41 | private XMPPTCPConnection connection;
42 | private String mServiceName;
43 |
44 | public FriendListAdapter(Context mContext,Collection entries) {
45 | this.mContext = mContext;
46 | mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
47 | mUsers = new ArrayList<>();
48 | mUserMap = new HashMap<>() ;
49 | connection = DataWarehouse.getXMPPTCPConnection(mContext);
50 | mServiceName = connection.getServiceName();
51 | if (entries != null)
52 | {
53 | Iterator iterator = entries.iterator();
54 | while (iterator.hasNext())
55 | {
56 | RosterEntry entry = iterator.next();
57 | //有的返回的用户JID后面包含@
58 | if (entry.getUser().indexOf("@") == -1)
59 | {
60 | UserData userData = new UserData(entry.getUser());
61 | mUserMap.put(entry.getUser(),entry.getName());
62 | mUsers.add(userData);
63 | }
64 | }
65 | }
66 | }
67 |
68 | @Override
69 | public int getCount() {
70 | return mUsers.size();
71 | }
72 |
73 | @Override
74 | public Object getItem(int position) {
75 | return mUsers.get(position);
76 | }
77 |
78 | @Override
79 | public long getItemId(int position) {
80 | return position;
81 | }
82 |
83 | //根据位置获取用户名
84 | public String getUsername(int position)
85 | {
86 | return mUsers.get(position).username;
87 | }
88 |
89 |
90 | //实现ListView的实时刷新。
91 | public void addUserData(UserData userData)
92 | {
93 | mUsers.add(userData);
94 | //如果adapter有数据更细,调用此函数可以更细adapter
95 | notifyDataSetChanged();
96 | }
97 |
98 | public void removeUserData(int position)
99 | {
100 | XMPPTCPConnection connection = DataWarehouse.getXMPPTCPConnection(mContext);
101 | RosterEntry entry = Roster.getInstanceFor(connection).getEntry(getUsername(position));
102 | if (entry != null)
103 | {
104 | try {
105 | Roster.getInstanceFor(connection).removeEntry(entry);
106 | }catch (Exception e)
107 | {
108 | e.printStackTrace();
109 | }
110 | mUsers.remove(position);
111 | notifyDataSetChanged();
112 | }
113 | }
114 |
115 | @Override
116 | public View getView(final int position, View convertView, ViewGroup parent) {
117 | if (convertView == null)
118 | {
119 | convertView = mLayoutInflater.inflate(R.layout.friend_list_item,null);
120 | }
121 | TextView user = (TextView) convertView.findViewById(R.id.textview_friend_list_item_name);
122 | user.setText(getUsername(position));
123 |
124 |
125 |
126 | return convertView;
127 | }
128 |
129 | //chatJid: friendUsername@serviceName
130 | private Chat createChat(String chatJid)
131 | {
132 | if (isConnected())
133 | {
134 | ChatManager chatManager = ChatManager.getInstanceFor(connection);
135 | return chatManager.createChat(chatJid);
136 | }
137 | throw new NullPointerException("连接服务器失败,请先连接服务器!");
138 | }
139 |
140 | private boolean isConnected()
141 | {
142 | if (connection == null)
143 | {
144 | return false;
145 | }
146 | if (!connection.isConnected())
147 | {
148 | try {
149 | connection.connect();
150 | return true;
151 | } catch (SmackException e) {
152 | e.printStackTrace();
153 | } catch (IOException e) {
154 | e.printStackTrace();
155 | } catch (XMPPException e) {
156 | e.printStackTrace();
157 | }
158 | }
159 | return true;
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/adapter/MultiUserChatRoomListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.example.adapter;
2 |
3 | import android.content.Context;
4 | import android.view.View;
5 | import android.view.ViewGroup;
6 | import android.widget.TextView;
7 |
8 | import com.example.activity.R;
9 | import com.example.data.ChatRoomData;
10 |
11 | import org.jivesoftware.smackx.disco.packet.DiscoverItems;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 |
17 | public class MultiUserChatRoomListAdapter extends ParentAdapter {
18 | private List mChatRoomList;
19 |
20 | public MultiUserChatRoomListAdapter(Context mContext, List chatRoomList) {
21 | super(mContext);
22 | mChatRoomList = new ArrayList<>();
23 | for (int i=0; i chatRoomList)
53 | {
54 | mChatRoomList.clear();
55 | for (int i=0; i
17 | */
18 | String FACE_TEXT_SUFFIX = ":>";
19 |
20 | String JID = "jid";
21 |
22 | String CHAT_ROOM_NAME = "chat_room_name"; // 聊天室名称
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/common/Storage.java:
--------------------------------------------------------------------------------
1 | package com.example.common;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 |
7 | public class Storage {
8 | private final static String STORAGE_FINAL_NAME = "chatclent.config";
9 |
10 | //获取SharedPreferences
11 | private static SharedPreferences getSharedPreference(Context ctx)
12 | {
13 | return ctx.getSharedPreferences(STORAGE_FINAL_NAME,Context.MODE_PRIVATE);
14 | }
15 |
16 | //往SharedPreferences中存放String类型的值。
17 | public static void putString(Context ctx,String key,String value)
18 | {
19 | SharedPreferences sharedPreferences = getSharedPreference(ctx);
20 | sharedPreferences.edit().putString(key,value).commit();
21 | }
22 |
23 | //获取键值时,可以设置默认值,也可以不设置默认值。故将默认值设置为String...可变参数。
24 | public static String getString(Context ctx,String key,String... defaultValue)
25 | {
26 | SharedPreferences sharedPreferences = getSharedPreference(ctx);
27 | //如果getString()不设置默认值,则将默认值设置为""。
28 | String dv = "";
29 | //如果getString()设置了默认值,则读取defaultValue中的第一个值。
30 | for(String v : defaultValue)
31 | {
32 | dv = v;
33 | break;
34 | }
35 | return sharedPreferences.getString(key,dv);
36 | }
37 |
38 | //往SharedPreferences中存放Boolean类型的数据。
39 | public static void putBollean(Context ctx,String key,Boolean value)
40 | {
41 | SharedPreferences sharedPreferences = getSharedPreference(ctx);
42 | sharedPreferences.edit().putBoolean(key,value).commit();
43 | }
44 |
45 | public static Boolean getBoolean(Context ctx,String key,Boolean... defaultValue)
46 | {
47 | SharedPreferences sharedPreferences = getSharedPreference(ctx);
48 | boolean dv = false;
49 | for (boolean v : defaultValue)
50 | {
51 | dv = v;
52 | break;
53 | }
54 | return sharedPreferences.getBoolean(key,dv);
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/common/Util.java:
--------------------------------------------------------------------------------
1 | package com.example.common;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.graphics.Matrix;
7 | import android.text.SpannableString;
8 | import android.text.Spanned;
9 | import android.text.style.ImageSpan;
10 | import android.widget.TextView;
11 |
12 | import com.example.activity.R;
13 |
14 | import java.lang.reflect.Field;
15 | import java.util.regex.Matcher;
16 | import java.util.regex.Pattern;
17 |
18 |
19 | public class Util {
20 | // 从包含服务名的user中(user2@thor)中提取@之前的内容
21 | public static String extractUserFromChat(String user)
22 | {
23 | int index = user.indexOf("@");
24 | if(index == -1)
25 | {
26 | return user;
27 | }
28 | else
29 | {
30 | return user.substring(0, index);
31 | }
32 | }
33 |
34 | //根据资源名获取资源ID
35 | public static int getResourceIDFromName(Class c,String name)
36 | {
37 | int resID = -1;
38 | try {
39 | //利用Java反射
40 | Field field = c.getField(name);
41 | resID = field.getInt(null);
42 | }
43 | catch (Exception e)
44 | {
45 | e.printStackTrace();
46 | }
47 |
48 | return resID;
49 | }
50 |
51 | public static void updateFacesForTextView(Context context, TextView textView)
52 | {
53 | // ab<:12:>xyz<:1:>abc
54 | String regExp = Const.FACE_TEXT_PREFIX + "\\d+" + Const.FACE_TEXT_SUFFIX;
55 |
56 | Pattern pattern = Pattern.compile(regExp);
57 |
58 | String oldText = textView.getText().toString();
59 |
60 | textView.setText("");
61 | String oldTextArray[] = oldText.split(regExp);
62 | Matcher matcher = pattern.matcher(oldText);
63 | int start = 0;
64 | int i = 0;
65 | int count = 0;
66 |
67 | while(matcher.find(start))
68 | {
69 | if(i < oldTextArray.length)
70 | {
71 | textView.append(oldTextArray[i++]);
72 | }
73 | count++;
74 | String faceText = matcher.group();
75 | int faceTextSuffixStartIndex = faceText.indexOf(Const.FACE_TEXT_SUFFIX);
76 | int faceId = Integer.parseInt(faceText.substring(Const.FACE_TEXT_PREFIX.length(),
77 | faceTextSuffixStartIndex));
78 | start = matcher.end();
79 | String faceResName = Const.FACE_PREFIX + faceId;
80 | int resId = Util.getResourceIDFromName(R.drawable.class, faceResName);
81 |
82 | if(resId == -1)
83 | {
84 | textView.append(faceText);
85 | continue;
86 | }
87 |
88 | Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),
89 | resId);
90 | Matrix matrix = new Matrix();
91 | matrix.postScale(0.6f, 0.6f);
92 | Bitmap smallBitmap = Bitmap.createBitmap(bitmap, 0,0, bitmap.getWidth(),
93 | bitmap.getHeight(),matrix,true);
94 |
95 | ImageSpan imageSpan = new ImageSpan(context, smallBitmap);
96 | SpannableString spannableString = new SpannableString(faceText);
97 | spannableString.setSpan(imageSpan, 0, faceText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
98 |
99 | textView.append(spannableString);
100 |
101 | }
102 |
103 | if(count == oldTextArray.length - 1)
104 | {
105 | textView.append(oldTextArray[count]);
106 | }
107 | }
108 |
109 | public static String extractUserFromChatGroup(String user)
110 | {
111 | int index = user.indexOf("@");
112 | if (index == -1)
113 | {
114 | return user;
115 | }
116 | else
117 | {
118 | int index1 = user.indexOf("/");
119 | if (index1 == -1)
120 | return user;
121 | else
122 | return user.substring(index1 + 1);
123 | }
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/common/XMPPUtil.java:
--------------------------------------------------------------------------------
1 | package com.example.common;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 | import com.example.data.DataWarehouse;
6 | import org.jivesoftware.smack.ConnectionConfiguration;
7 | import org.jivesoftware.smack.ConnectionListener;
8 | import org.jivesoftware.smack.SmackException;
9 | import org.jivesoftware.smack.XMPPConnection;
10 | import org.jivesoftware.smack.XMPPException;
11 | import org.jivesoftware.smack.tcp.XMPPTCPConnection;
12 | import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
13 | import java.io.IOException;
14 |
15 |
16 |
17 | public class XMPPUtil {
18 |
19 | private static String host = "192.168.0.5";
20 | private static int port = 5222;
21 | private static String serviceName = "192.168.0.5";
22 |
23 |
24 |
25 | //连接服务器
26 | public static XMPPTCPConnection getXMPPConnection(Context ctx) {
27 |
28 | // SmackConfiguration.DEBUG = true;
29 | XMPPTCPConnectionConfiguration.Builder configBuilder = XMPPTCPConnectionConfiguration.builder();
30 | //设置服务器IP地址
31 | configBuilder.setHost(host);
32 | //设置服务器端口
33 | configBuilder.setPort(port);
34 | //设置服务器名称
35 | configBuilder.setServiceName(serviceName);
36 | //设置开启调试
37 | configBuilder.setDebuggerEnabled(true);
38 | //设置开启压缩,可以节省流量
39 | configBuilder.setCompressionEnabled(true);
40 | //关闭安全验证
41 | configBuilder.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
42 |
43 | XMPPTCPConnection connection = new XMPPTCPConnection(configBuilder.build());
44 |
45 | connection.addConnectionListener(new ConnectionListener() {
46 | @Override
47 | public void connected(XMPPConnection xmppConnection) {
48 | Log.e("connect","connected");
49 | }
50 |
51 | @Override
52 | public void authenticated(XMPPConnection xmppConnection, boolean b) {
53 | Log.e("connect","authenticated");
54 | }
55 |
56 | @Override
57 | public void connectionClosed() {
58 | Log.e("connect","connectionClosed");
59 | }
60 |
61 | @Override
62 | public void connectionClosedOnError(Exception e) {
63 | Log.e("connect","connectionClosedOnError");
64 | }
65 |
66 | @Override
67 | public void reconnectionSuccessful() {
68 | Log.e("connect","reconnectionSuccessful");
69 | }
70 |
71 | @Override
72 | public void reconnectingIn(int i) {
73 | Log.e("connect","reconnectionIn: "+i);
74 | }
75 |
76 | @Override
77 | public void reconnectionFailed(Exception e) {
78 | Log.e("connect","reconnectionFailed");
79 | }
80 | });
81 |
82 | try {
83 | connection.connect();
84 | } catch (SmackException | IOException | XMPPException e) {
85 | e.printStackTrace();
86 | return null;
87 | }
88 | return connection;
89 | }
90 |
91 |
92 | public static boolean login(Context ctx, String username, String password) {
93 | XMPPTCPConnection connection = getXMPPConnection(ctx);
94 | if (connection == null)
95 | {
96 | Log.e("login","connection == null");
97 | return false;
98 | }
99 | try {
100 | connection.login(username, password);
101 | DataWarehouse.setXMPPTCPConnection(ctx,connection);
102 | return true;
103 | } catch (XMPPException|SmackException|IOException e) {
104 | e.printStackTrace();
105 | Log.e("login","login failure");
106 | return false;
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/data/ChatData.java:
--------------------------------------------------------------------------------
1 | package com.example.data;
2 |
3 |
4 |
5 | public class ChatData {
6 | public String user; // 用于账号
7 | public String text; // 聊天文本
8 | public boolean isOwner; // 是否是当前登录用户
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/data/ChatRoomData.java:
--------------------------------------------------------------------------------
1 | package com.example.data;
2 |
3 | import org.jivesoftware.smackx.disco.packet.DiscoverItems;
4 |
5 |
6 | public class ChatRoomData {
7 | public DiscoverItems.Item item;
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/data/ChatServiceData.java:
--------------------------------------------------------------------------------
1 | package com.example.data;
2 |
3 | import org.jivesoftware.smackx.muc.HostedRoom;
4 |
5 |
6 | public class ChatServiceData {
7 | public HostedRoom hostedRoom;
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/data/DataWarehouse.java:
--------------------------------------------------------------------------------
1 | package com.example.data;
2 |
3 | import android.content.Context;
4 |
5 | import com.example.MyApplication;
6 |
7 | import org.jivesoftware.smack.tcp.XMPPTCPConnection;
8 |
9 |
10 | public class DataWarehouse {
11 | public static MyApplication getGlobalData(Context ctx)
12 | {
13 | return (MyApplication) ctx.getApplicationContext();
14 | }
15 |
16 | public static XMPPTCPConnection getXMPPTCPConnection(Context ctx)
17 | {
18 | return getGlobalData(ctx).xmpptcpConnection;
19 | }
20 |
21 | public static void setXMPPTCPConnection(Context ctx,XMPPTCPConnection conn)
22 | {
23 | getGlobalData(ctx).xmpptcpConnection = conn;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/data/LoginData.java:
--------------------------------------------------------------------------------
1 | package com.example.data;
2 |
3 |
4 | public class LoginData {
5 | public static String userName;
6 | public static String passWord;
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/data/UserData.java:
--------------------------------------------------------------------------------
1 | package com.example.data;
2 |
3 |
4 | public class UserData {
5 | public String username; //用户名
6 |
7 | public UserData(String username) {
8 | this.username = username;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/side_nav_bar.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_add_friend.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
16 |
17 |
18 |
19 |
25 |
26 |
27 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_chat.xml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
19 |
20 |
25 |
26 |
33 |
34 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_chat_service.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_face.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
18 |
19 |
20 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
15 |
16 |
23 |
24 |
31 |
32 |
39 |
40 |
48 |
49 |
50 |
51 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
17 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_multi_user_chat_room.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
20 |
21 |
27 |
28 |
29 |
38 |
39 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_multi_user_chat_rooms.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
23 |
24 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_register.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
25 |
26 |
33 |
34 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_video_call.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/chat_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
17 |
18 |
25 |
26 |
36 |
37 |
46 |
47 |
48 |
49 |
58 |
59 |
66 |
67 |
77 |
78 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/chat_service_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/friend_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/multi_user_chat_room_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/activity_navigation_drawer.xml:
--------------------------------------------------------------------------------
1 |
2 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/friend_list_context_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_chat.xml:
--------------------------------------------------------------------------------
1 |
5 |
9 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_chat_room.xml:
--------------------------------------------------------------------------------
1 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
5 |
9 |
13 |
14 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/navigation.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 | 16dp
7 | 160dp
8 | 16dp
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/drawables.xml:
--------------------------------------------------------------------------------
1 |
2 | - @android:drawable/ic_menu_camera
3 | - @android:drawable/ic_menu_gallery
4 | - @android:drawable/ic_menu_slideshow
5 | - @android:drawable/ic_menu_manage
6 | - @android:drawable/ic_menu_share
7 | - @android:drawable/ic_menu_send
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SmackWebRtc
3 |
4 | Accept Unknown Certificate?
5 | The server certificate is not signed by a known Certificate Authority.
6 | The server certificate is expired.
7 | Accept Mismatching Server Name?
8 | Server could not authenticate as \"%s\". The certificate is only valid for:
9 |
10 | Do you want to connect anyway?
11 | Certificate details:
12 |
13 | Always
14 | Once
15 | Abort
16 |
17 | Certificate Verification
18 | NavigationActivity
19 |
20 | Open navigation drawer
21 | Close navigation drawer
22 |
23 | Settings
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/test/java/com/example/smack/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.example.activity;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/by666/LocalVideo_Android/d4f4e1b825a0f539658452eab22260fa6a31e6b6/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Sep 11 14:06:01 CST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/local.properties:
--------------------------------------------------------------------------------
1 | ## This file must *NOT* be checked into Version Control Systems,
2 | # as it contains information specific to your local configuration.
3 | #
4 | # Location of the SDK. This is only used by Gradle.
5 | # For customization when using a Version Control System, please read the
6 | # header note.
7 | #Tue Sep 11 14:05:38 CST 2018
8 | ndk.dir=/Users/by.huang/Library/Android/sdk/ndk-bundle
9 | sdk.dir=/Users/by.huang/Library/Android/sdk
10 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/webrtc_test.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------