├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── amp1.png
│ │ │ │ ├── amp2.png
│ │ │ │ ├── amp3.png
│ │ │ │ ├── amp4.png
│ │ │ │ ├── amp5.png
│ │ │ │ ├── amp6.png
│ │ │ │ ├── amp7.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_arrow_left.png
│ │ │ │ ├── chatting_emoji_l.png
│ │ │ │ ├── chatting_emoji_n.png
│ │ │ │ ├── chatting_extra_n.png
│ │ │ │ ├── chatting_extra_p.png
│ │ │ │ ├── chatting_voice_n.png
│ │ │ │ ├── chatting_voice_p.png
│ │ │ │ ├── rcd_cancel_bg.9.png
│ │ │ │ ├── rcd_cancel_icon.png
│ │ │ │ ├── voice_rcd_hint.png
│ │ │ │ ├── voice_to_short.png
│ │ │ │ ├── chatting_keyboard_n.png
│ │ │ │ ├── chatting_keyboard_p.png
│ │ │ │ └── voice_rcd_cancel_bg.png
│ │ │ ├── drawable
│ │ │ │ ├── rcd_cancel_bg.9.png
│ │ │ │ ├── voice_rcd_hint_bg.9.png
│ │ │ │ ├── chatting_emoji.xml
│ │ │ │ ├── im_msg_text_send_bg_sel.xml
│ │ │ │ ├── im_msg_text_receive_bg_sel.xml
│ │ │ │ ├── chatting_send.xml
│ │ │ │ └── im_progress_small_white.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── im_ic_failed.png
│ │ │ │ ├── im_msg_time_bg_tip.9.png
│ │ │ │ ├── im_loading_small_white.png
│ │ │ │ ├── im_msg_text_send_bg_normal.9.png
│ │ │ │ ├── im_msg_text_receive_bg_normal.9.png
│ │ │ │ ├── im_msg_text_send_bg_pressed.9.png
│ │ │ │ └── im_msg_text_receive_bg_pressed.9.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── layout
│ │ │ │ ├── item_empty.xml
│ │ │ │ ├── im_msg_item_picture.xml
│ │ │ │ ├── content_main.xml
│ │ │ │ ├── fragment_chatting.xml
│ │ │ │ ├── im_extra_actions_item_layout.xml
│ │ │ │ ├── im_msg_item_text.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── activity_chatting.xml
│ │ │ │ ├── pop_chatting_voice.xml
│ │ │ │ ├── chatting_footer.xml
│ │ │ │ └── im_msg_item.xml
│ │ │ ├── values-v21
│ │ │ │ └── styles.xml
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── dimens.xml
│ │ │ └── menu
│ │ │ │ └── menu_main.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── saysay
│ │ │ │ └── ljh
│ │ │ │ └── chattingui
│ │ │ │ ├── message
│ │ │ │ ├── model
│ │ │ │ │ ├── MessageBody.java
│ │ │ │ │ ├── ModuleProxy.java
│ │ │ │ │ ├── MessagePictureBody.java
│ │ │ │ │ ├── MessageTextBody.java
│ │ │ │ │ ├── Container.java
│ │ │ │ │ ├── IMessage.java
│ │ │ │ │ ├── MsgDirectionEnum.java
│ │ │ │ │ ├── MsgStatusEnum.java
│ │ │ │ │ ├── MsgTypeEnum.java
│ │ │ │ │ └── SimpleMessage.java
│ │ │ │ ├── holder
│ │ │ │ │ ├── UnknownHolder.java
│ │ │ │ │ ├── MessageTextHolder.java
│ │ │ │ │ ├── MessagePictureHolder.java
│ │ │ │ │ └── BaseMessageHolder.java
│ │ │ │ ├── actions
│ │ │ │ │ ├── PickImageAction.java
│ │ │ │ │ └── BaseAction.java
│ │ │ │ └── adapter
│ │ │ │ │ ├── ExtarActionsGridAdapter.java
│ │ │ │ │ ├── ExtraActionsPagerAdapter.java
│ │ │ │ │ └── MessageAdapter.java
│ │ │ │ ├── utils
│ │ │ │ ├── PrefUtils.java
│ │ │ │ ├── BitmapDecoder.java
│ │ │ │ ├── ScreenUtil.java
│ │ │ │ ├── SampleSizeUtil.java
│ │ │ │ └── TimeUtil.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── widgets
│ │ │ │ ├── EditTextPreIme.java
│ │ │ │ ├── PointIndicator.java
│ │ │ │ └── ChattingFooter.java
│ │ │ │ ├── ChattingActivity.java
│ │ │ │ └── ChattingFragment.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── saysay
│ │ │ └── ljh
│ │ │ └── chattingui
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── saysay
│ │ └── ljh
│ │ └── chattingui
│ │ └── ApplicationTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── .idea
├── .web2png
│ ├── webp-imageio.dll
│ ├── libwebp-imageio.so
│ ├── libwebp-imageio.dylib
│ ├── webp-imageio-0.4.2.zip
│ └── webp-imageio-0.4.2
│ │ ├── webp-imageio.jar
│ │ ├── linux-x64
│ │ └── libwebp-imageio.so
│ │ ├── linux-x86
│ │ └── libwebp-imageio.so
│ │ ├── windows-x64
│ │ └── webp-imageio.dll
│ │ ├── windows-x86
│ │ └── webp-imageio.dll
│ │ └── macosx-universal
│ │ └── libwebp-imageio.dylib
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── vcs.xml
├── inspectionProfiles
│ ├── profiles_settings.xml
│ └── Project_Default.xml
├── modules.xml
├── runConfigurations.xml
├── gradle.xml
├── compiler.xml
└── misc.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/.idea/.web2png/webp-imageio.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/.idea/.web2png/webp-imageio.dll
--------------------------------------------------------------------------------
/.idea/.web2png/libwebp-imageio.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/.idea/.web2png/libwebp-imageio.so
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/.idea/.web2png/libwebp-imageio.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/.idea/.web2png/libwebp-imageio.dylib
--------------------------------------------------------------------------------
/.idea/.web2png/webp-imageio-0.4.2.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/.idea/.web2png/webp-imageio-0.4.2.zip
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/amp1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/amp1.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/amp2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/amp2.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/amp3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/amp3.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/amp4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/amp4.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/amp5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/amp5.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/amp6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/amp6.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/amp7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/amp7.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rcd_cancel_bg.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/drawable/rcd_cancel_bg.9.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/im_ic_failed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/drawable-hdpi/im_ic_failed.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_arrow_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/ic_arrow_left.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/.idea/.web2png/webp-imageio-0.4.2/webp-imageio.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/.idea/.web2png/webp-imageio-0.4.2/webp-imageio.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable/voice_rcd_hint_bg.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/drawable/voice_rcd_hint_bg.9.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/chatting_emoji_l.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/chatting_emoji_l.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/chatting_emoji_n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/chatting_emoji_n.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/chatting_extra_n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/chatting_extra_n.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/chatting_extra_p.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/chatting_extra_p.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/chatting_voice_n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/chatting_voice_n.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/chatting_voice_p.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/chatting_voice_p.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/rcd_cancel_bg.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/rcd_cancel_bg.9.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/rcd_cancel_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/rcd_cancel_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/voice_rcd_hint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/voice_rcd_hint.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/voice_to_short.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/voice_to_short.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/im_msg_time_bg_tip.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/drawable-hdpi/im_msg_time_bg_tip.9.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/chatting_keyboard_n.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/chatting_keyboard_n.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/chatting_keyboard_p.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/chatting_keyboard_p.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/voice_rcd_cancel_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/mipmap-xxhdpi/voice_rcd_cancel_bg.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/im_loading_small_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/drawable-hdpi/im_loading_small_white.png
--------------------------------------------------------------------------------
/.idea/.web2png/webp-imageio-0.4.2/linux-x64/libwebp-imageio.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/.idea/.web2png/webp-imageio-0.4.2/linux-x64/libwebp-imageio.so
--------------------------------------------------------------------------------
/.idea/.web2png/webp-imageio-0.4.2/linux-x86/libwebp-imageio.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/.idea/.web2png/webp-imageio-0.4.2/linux-x86/libwebp-imageio.so
--------------------------------------------------------------------------------
/.idea/.web2png/webp-imageio-0.4.2/windows-x64/webp-imageio.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/.idea/.web2png/webp-imageio-0.4.2/windows-x64/webp-imageio.dll
--------------------------------------------------------------------------------
/.idea/.web2png/webp-imageio-0.4.2/windows-x86/webp-imageio.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/.idea/.web2png/webp-imageio-0.4.2/windows-x86/webp-imageio.dll
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/im_msg_text_send_bg_normal.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/drawable-hdpi/im_msg_text_send_bg_normal.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/im_msg_text_receive_bg_normal.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/drawable-hdpi/im_msg_text_receive_bg_normal.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/im_msg_text_send_bg_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/drawable-hdpi/im_msg_text_send_bg_pressed.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/im_msg_text_receive_bg_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/app/src/main/res/drawable-hdpi/im_msg_text_receive_bg_pressed.9.png
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/.web2png/webp-imageio-0.4.2/macosx-universal/libwebp-imageio.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ljhgithub/ChattingUI/HEAD/.idea/.web2png/webp-imageio-0.4.2/macosx-universal/libwebp-imageio.dylib
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/model/MessageBody.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.model;
2 |
3 | /**
4 | * Created by ljh on 2016/10/19.
5 | */
6 |
7 | public class MessageBody {
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_empty.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/model/ModuleProxy.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.model;
2 |
3 | /**
4 | * Created by ljh on 2016/10/19.
5 | */
6 |
7 | public interface ModuleProxy {
8 | public void sendMessage(IMessage message);
9 | }
10 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Oct 17 11:54:33 CST 2016
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-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/chatting_emoji.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/im_msg_text_send_bg_sel.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/im_msg_text_receive_bg_sel.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 | >
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ChattingUI
3 | Settings
4 |
5 | 手指上滑,取消发送
6 | 松开手指,取消发送
7 | 录音时间太短
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/holder/UnknownHolder.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.holder;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.View;
5 |
6 | /**
7 | * Created by ljh on 2015/12/17.
8 | */
9 | public class UnknownHolder extends RecyclerView.ViewHolder {
10 | public UnknownHolder(View itemView) {
11 | super(itemView);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/test/java/com/saysay/ljh/chattingui/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui;
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 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/model/MessagePictureBody.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.model;
2 |
3 | /**
4 | * Created by ljh on 2016/10/19.
5 | */
6 |
7 | public class MessagePictureBody extends MessageBody {
8 |
9 | private String path = "";
10 |
11 | public String getPath() {
12 | return path;
13 | }
14 |
15 | public void setPath(String url) {
16 | this.path = url;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/saysay/ljh/chattingui/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui;
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/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/model/MessageTextBody.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.model;
2 |
3 | /**
4 | * Created by ljh on 2016/10/19.
5 | */
6 |
7 | public class MessageTextBody extends MessageBody {
8 |
9 | private String content = "";
10 |
11 | public void setContent(String content) {
12 | this.content = content;
13 | }
14 |
15 | public String getContent() {
16 | return content;
17 | }
18 |
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/chatting_send.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 | -
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 | #00000000
7 |
8 | #00CD00
9 | #8000CD00
10 |
11 | #dddddd
12 | #fff
13 |
14 | #b3000000
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/model/Container.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.model;
2 |
3 | import android.app.Activity;
4 |
5 | /**
6 | * Created by ljh on 2016/10/19.
7 | */
8 |
9 | public class Container {
10 | public final Activity activity;
11 | public final String account;
12 | public final ModuleProxy proxy;
13 |
14 | public Container(Activity activity, String account, ModuleProxy proxy) {
15 | this.activity = activity;
16 | this.account = account;
17 | this.proxy = proxy;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/actions/PickImageAction.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.actions;
2 |
3 | import android.util.Log;
4 |
5 | import com.saysay.ljh.chattingui.message.model.SimpleMessage;
6 |
7 | /**
8 | * Created by ljh on 2016/10/19.
9 | */
10 |
11 | public class PickImageAction extends BaseAction {
12 | public PickImageAction(int iconResId, int titleId) {
13 | super(iconResId, titleId);
14 | }
15 |
16 | @Override
17 | public void onClick() {
18 | Log.d("tag","dddddddddddddddd");
19 | sendMessage(new SimpleMessage());
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/im_msg_item_picture.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/model/IMessage.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.model;
2 |
3 | /**
4 | * Created by ljh on 2016/10/19.
5 | */
6 |
7 | public interface IMessage {
8 | public void setMessageBody(MessageBody body);
9 | public MessageBody getMessageBody();
10 | public long getTime();
11 |
12 | public void setTime(Long time);
13 |
14 | public MsgDirectionEnum getDirection();
15 |
16 | public void setDirection(MsgDirectionEnum direction);
17 |
18 | public MsgStatusEnum getStatus();
19 |
20 | public void setStatus(MsgStatusEnum status);
21 |
22 | public MsgTypeEnum getType();
23 |
24 | public void setType(MsgTypeEnum type);
25 | }
26 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/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:\Program Files\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 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_chatting.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/im_extra_actions_item_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 16dp
6 |
7 |
8 |
9 | 60dip
10 | 50dip
11 | 40dip
12 | 30dip
13 | 60dip
14 |
15 |
16 | 223dip
17 | 18dip
18 | 8dip
19 | 8dip
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/model/MsgDirectionEnum.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.model;
2 |
3 | /**
4 | * Created by ljh on 2016/10/19.
5 | */
6 |
7 | public enum MsgDirectionEnum {
8 | SEND(0),
9 | RECEIVE(1);
10 |
11 | private int value;
12 |
13 | private MsgDirectionEnum(int var3) {
14 | this.value = var3;
15 | }
16 |
17 | public final int getValue() {
18 | return this.value;
19 | }
20 |
21 | public static MsgDirectionEnum directionOfValue(int var0) {
22 | MsgDirectionEnum[] var1;
23 | int var2 = (var1 = values()).length;
24 |
25 | for(int var3 = 0; var3 < var2; ++var3) {
26 | MsgDirectionEnum var4;
27 | if((var4 = var1[var3]).getValue() == var0) {
28 | return var4;
29 | }
30 | }
31 |
32 | return RECEIVE;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 23
5 | buildToolsVersion "23.0.2"
6 |
7 | defaultConfig {
8 | applicationId "com.saysay.ljh.chattingui"
9 | minSdkVersion 15
10 | targetSdkVersion 23
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | testCompile 'junit:junit:4.12'
25 | compile 'com.android.support:appcompat-v7:24.2.1'
26 | compile 'com.android.support:design:24.2.1'
27 | compile 'com.android.support:recyclerview-v7:24.2.1'
28 | compile 'com.github.bumptech.glide:glide:3.7.0'
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/im_msg_item_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
21 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/model/MsgStatusEnum.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.model;
2 |
3 | /**
4 | * Created by ljh on 2016/10/19.
5 | */
6 |
7 | public enum MsgStatusEnum {
8 | DRAFT(-1),
9 | SENDING(0),
10 | SUCCESS(1),
11 | FAIL(2),
12 | READ(3),
13 | UNREAD(4);
14 |
15 | private int value;
16 |
17 | private MsgStatusEnum(int var3) {
18 | this.value = var3;
19 | }
20 |
21 | public static MsgStatusEnum statusOfValue(int var0) {
22 | MsgStatusEnum[] var1;
23 | int var2 = (var1 = values()).length;
24 |
25 | for(int var3 = 0; var3 < var2; ++var3) {
26 | MsgStatusEnum var4;
27 | if((var4 = var1[var3]).getValue() == var0) {
28 | return var4;
29 | }
30 | }
31 |
32 | return SENDING;
33 | }
34 |
35 | public final int getValue() {
36 | return this.value;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/im_progress_small_white.xml:
--------------------------------------------------------------------------------
1 |
2 |
20 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/utils/PrefUtils.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.utils;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.preference.PreferenceManager;
6 | import android.support.v4.content.ContextCompat;
7 |
8 | /**
9 | * Created by ljh on 2016/1/27.
10 | */
11 | public class PrefUtils {
12 |
13 | private final static String KEYBOARD_HEIGHT = "preference_keyboard_height";
14 |
15 | private PrefUtils() {
16 | }
17 |
18 | private static SharedPreferences getPreferences(Context context) {
19 | return context.getSharedPreferences(context.getPackageName() + "_preferences", Context.MODE_PRIVATE);
20 | }
21 |
22 | public static void setKeyboardHeight(Context context, int height) {
23 | getPreferences(context).edit().putInt(KEYBOARD_HEIGHT, height).commit();
24 | }
25 |
26 | public static int getKeyboardHeight(Context context) {
27 | return getPreferences(context).getInt(KEYBOARD_HEIGHT, 0);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/holder/MessageTextHolder.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.holder;
2 |
3 | import android.view.View;
4 | import android.widget.TextView;
5 |
6 | import com.saysay.ljh.chattingui.R;
7 | import com.saysay.ljh.chattingui.message.model.IMessage;
8 | import com.saysay.ljh.chattingui.message.model.MessageTextBody;
9 |
10 | /**
11 | * Created by ljh on 2016/10/19.
12 | */
13 |
14 | public class MessageTextHolder extends BaseMessageHolder {
15 | private TextView msgTextContent;
16 |
17 | public MessageTextHolder(View itemView) {
18 | super(itemView);
19 | View view = View.inflate(itemView.getContext(), R.layout.im_msg_item_text, msgContent);
20 | msgTextContent = (TextView) view.findViewById(R.id.msg_text_content);
21 | }
22 |
23 | @Override
24 | public void bindMessage(IMessage iMessage) {
25 | super.bindMessage(iMessage);
26 | MessageTextBody messageText = (MessageTextBody) iMessage.getMessageBody();
27 | msgTextContent.setText(messageText.getContent());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/model/MsgTypeEnum.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.model;
2 |
3 | /**
4 | * Created by ljh on 2016/10/19.
5 | */
6 |
7 | public enum MsgTypeEnum {
8 | UNDEF(-1, "Unknown"),
9 | TEXT(0, ""),
10 | IMAGE(1, "图片"),
11 | AUDIO(2, "语音"),
12 | VIDEO(3, "视频"),
13 | LOCATION(4, "位置"),
14 | FILE(6, "文件"),
15 | AVCHAT(7, "音视频通话"),
16 | NOTIFICATION(5, "通知消息"),
17 | TIP(10, "提醒消息"),
18 | CUSTOM(100, "自定义消息");
19 |
20 | private final int value;
21 | final String sendMessageTip;
22 |
23 | private MsgTypeEnum(int var3, String var4) {
24 | this.value = var3;
25 | this.sendMessageTip = var4;
26 | }
27 |
28 | public final int getValue() {
29 | return this.value;
30 | }
31 |
32 | public final String getSendMessageTip() {
33 | return this.sendMessageTip;
34 | }
35 |
36 | public static MsgTypeEnum typeOfValue(int var0) {
37 | MsgTypeEnum[] var1;
38 | int var2 = (var1 = values()).length;
39 |
40 | for (int var3 = 0; var3 < var2; ++var3) {
41 | MsgTypeEnum var4;
42 | if ((var4 = var1[var3]).getValue() == var0) {
43 | return var4;
44 | }
45 | }
46 |
47 | return UNDEF;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/actions/BaseAction.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.actions;
2 |
3 | import com.saysay.ljh.chattingui.message.model.Container;
4 | import com.saysay.ljh.chattingui.message.model.IMessage;
5 |
6 | /**
7 | * Created by ljh on 2016/10/19.
8 | */
9 |
10 | public abstract class BaseAction {
11 |
12 | private int iconResId;
13 |
14 | private int titleId;
15 |
16 | private Container container;
17 |
18 |
19 | public Container getContainer() {
20 | return container;
21 | }
22 |
23 | public void setContainer(Container container) {
24 | this.container = container;
25 | }
26 |
27 |
28 | protected BaseAction(int iconResId, int titleId) {
29 | this.iconResId = iconResId;
30 | this.titleId = titleId;
31 | }
32 |
33 | public int getIconResId() {
34 | return iconResId;
35 | }
36 |
37 | public void setIconResId(int iconResId) {
38 | this.iconResId = iconResId;
39 | }
40 |
41 | public int getTitleId() {
42 | return titleId;
43 | }
44 |
45 | public void setTitleId(int titleId) {
46 | this.titleId = titleId;
47 | }
48 |
49 | public abstract void onClick();
50 |
51 | public void sendMessage(IMessage message) {
52 | container.proxy.sendMessage(message);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_chatting.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
20 |
21 |
26 |
27 |
28 |
29 |
34 |
35 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/holder/MessagePictureHolder.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.holder;
2 |
3 | import android.view.View;
4 | import android.widget.ImageView;
5 |
6 | import com.saysay.ljh.chattingui.R;
7 | import com.saysay.ljh.chattingui.message.model.IMessage;
8 | import com.saysay.ljh.chattingui.message.model.MessagePictureBody;
9 | import com.saysay.ljh.chattingui.utils.BitmapDecoder;
10 | import com.saysay.ljh.chattingui.utils.ScreenUtil;
11 |
12 |
13 | /**
14 | * Created by ljh on 2016/10/19.
15 | */
16 |
17 | public class MessagePictureHolder extends BaseMessageHolder {
18 | private ImageView msgThumbnail;
19 |
20 | public MessagePictureHolder(View itemView) {
21 | super(itemView);
22 | View view = View.inflate(itemView.getContext(), R.layout.im_msg_item_picture, msgContent);
23 | msgThumbnail = (ImageView) view.findViewById(R.id.msg_thumbnail);
24 | }
25 |
26 | @Override
27 | public void bindMessage(IMessage iMessage) {
28 | super.bindMessage(iMessage);
29 | MessagePictureBody msgPictureBody = (MessagePictureBody) iMessage.getMessageBody();
30 | msgThumbnail.setImageBitmap(BitmapDecoder.decodeSampled(msgPictureBody.getPath(), getImageMaxEdge(), getImageMaxEdge()));
31 | }
32 |
33 | public static int getImageMaxEdge() {
34 | return (int) (165.0 / 320.0 * ScreenUtil.screenWidth);
35 | }
36 |
37 | public static int getImageMinEdge() {
38 | return (int) (76.0 / 320.0 * ScreenUtil.screenWidth);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/model/SimpleMessage.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.model;
2 |
3 | /**
4 | * Created by ljh on 2016/10/19.
5 | */
6 |
7 | public class SimpleMessage implements IMessage {
8 | private Long time;
9 | private MsgDirectionEnum direction;
10 | private MsgStatusEnum status;
11 | private MsgTypeEnum type;
12 | private MessageBody messageBody;
13 |
14 | @Override
15 | public void setMessageBody(MessageBody body) {
16 | this.messageBody = body;
17 | }
18 |
19 | @Override
20 | public MessageBody getMessageBody() {
21 | return messageBody;
22 | }
23 |
24 | @Override
25 | public long getTime() {
26 | return time;
27 | }
28 |
29 | @Override
30 | public void setTime(Long time) {
31 | this.time = time;
32 | }
33 |
34 | @Override
35 | public MsgDirectionEnum getDirection() {
36 | return direction;
37 | }
38 |
39 | @Override
40 | public void setDirection(MsgDirectionEnum direction) {
41 | this.direction = direction;
42 | }
43 |
44 | @Override
45 | public MsgStatusEnum getStatus() {
46 | return status;
47 | }
48 |
49 | @Override
50 | public void setStatus(MsgStatusEnum status) {
51 | this.status = status;
52 | }
53 |
54 | @Override
55 | public MsgTypeEnum getType() {
56 | return type;
57 | }
58 |
59 | @Override
60 | public void setType(MsgTypeEnum type) {
61 | this.type = type;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.support.v7.widget.Toolbar;
6 | import android.view.Menu;
7 | import android.view.MenuItem;
8 |
9 | import com.saysay.ljh.chattingui.utils.ScreenUtil;
10 |
11 | public class MainActivity extends AppCompatActivity {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_main);
17 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
18 | setSupportActionBar(toolbar);
19 | ScreenUtil.init(this);
20 |
21 | }
22 |
23 | @Override
24 | public boolean onCreateOptionsMenu(Menu menu) {
25 | // Inflate the menu; this adds items to the action bar if it is present.
26 | getMenuInflater().inflate(R.menu.menu_main, menu);
27 | return true;
28 | }
29 |
30 | @Override
31 | public boolean onOptionsItemSelected(MenuItem item) {
32 | // Handle action bar item clicks here. The action bar will
33 | // automatically handle clicks on the Home/Up button, so long
34 | // as you specify a parent activity in AndroidManifest.xml.
35 | int id = item.getItemId();
36 |
37 | //noinspection SimplifiableIfStatement
38 | if (id == R.id.action_settings) {
39 | return true;
40 | }
41 |
42 | return super.onOptionsItemSelected(item);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/widgets/EditTextPreIme.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.widgets;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.KeyEvent;
6 | import android.widget.EditText;
7 |
8 | /**
9 | * Created by ljh on 2016/1/4.
10 | */
11 | public class EditTextPreIme extends EditText {
12 |
13 | private OnKeyBackListener onKeyBackListener;
14 |
15 | public EditTextPreIme(Context context) {
16 | super(context);
17 | }
18 |
19 | public EditTextPreIme(Context context, AttributeSet attrs) {
20 | super(context, attrs);
21 | }
22 |
23 | public EditTextPreIme(Context context, AttributeSet attrs, int defStyle) {
24 | super(context, attrs, defStyle);
25 | }
26 |
27 | public void setOnKeyBackListener(OnKeyBackListener onKeyBackListener) {
28 | this.onKeyBackListener = onKeyBackListener;
29 |
30 | }
31 |
32 | @Override
33 | public boolean dispatchKeyEventPreIme(KeyEvent event) {
34 | if (event.getAction() == KeyEvent.ACTION_UP && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
35 | // ((Activity) this.getContext()).onKeyDown(KeyEvent.KEYCODE_BACK, event);
36 | if (null != onKeyBackListener) {
37 | return onKeyBackListener.onKeyBack();
38 | } else {
39 | return super.dispatchKeyEvent(event);
40 | }
41 | } else {
42 | return super.dispatchKeyEvent(event);
43 | }
44 | }
45 |
46 | protected interface OnKeyBackListener {
47 | public boolean onKeyBack();
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
15 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/adapter/ExtarActionsGridAdapter.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.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.saysay.ljh.chattingui.R;
12 | import com.saysay.ljh.chattingui.message.actions.BaseAction;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * Created by ljh on 2016/10/19.
18 | */
19 |
20 | public class ExtarActionsGridAdapter extends BaseAdapter {
21 | private Context context;
22 |
23 | private List baseActions;
24 |
25 | public ExtarActionsGridAdapter(Context context, List baseActions) {
26 | this.context = context;
27 | this.baseActions = baseActions;
28 | }
29 |
30 | @Override
31 | public int getCount() {
32 | return baseActions.size();
33 | }
34 |
35 | @Override
36 | public Object getItem(int position) {
37 | return baseActions.get(position);
38 | }
39 |
40 | @Override
41 | public long getItemId(int position) {
42 | return position;
43 | }
44 |
45 | @Override
46 | public View getView(int position, View convertView, ViewGroup parent) {
47 | View itemlayout;
48 | if (convertView == null) {
49 | itemlayout = LayoutInflater.from(context).inflate(R.layout.im_extra_actions_item_layout, null);
50 | } else {
51 | itemlayout = convertView;
52 | }
53 |
54 | BaseAction viewHolder = baseActions.get(position);
55 | ((ImageView) itemlayout.findViewById(R.id.imageView)).setBackgroundResource(viewHolder.getIconResId());
56 | ((TextView) itemlayout.findViewById(R.id.textView)).setText(context.getString(viewHolder.getTitleId()));
57 | return itemlayout;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/utils/BitmapDecoder.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.utils;
2 |
3 | import android.annotation.TargetApi;
4 | import android.graphics.Bitmap;
5 | import android.graphics.BitmapFactory;
6 | import android.os.Build;
7 |
8 | /**
9 | * Created by ljh on 2016/10/20.
10 | */
11 |
12 | public class BitmapDecoder {
13 | public static Bitmap decodeSampled(String pathName, int reqWidth, int reqHeight) {
14 | return decodeSampled(pathName, getSampleSize(pathName, reqWidth, reqHeight));
15 | }
16 |
17 | public static Bitmap decodeSampled(String pathName, int sampleSize) {
18 | BitmapFactory.Options options = new BitmapFactory.Options();
19 |
20 | // RGB_565
21 | options.inPreferredConfig = Bitmap.Config.RGB_565;
22 | // sample size
23 | options.inSampleSize = sampleSize;
24 |
25 | Bitmap bitmap = null;
26 | try {
27 | bitmap = BitmapFactory.decodeFile(pathName, options);
28 | } catch (OutOfMemoryError e) {
29 | e.printStackTrace();
30 | return null;
31 | }
32 |
33 | return checkInBitmap(bitmap, options, pathName);
34 | }
35 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
36 | private static Bitmap checkInBitmap(Bitmap bitmap,
37 | BitmapFactory.Options options, String path) {
38 | boolean honeycomb = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
39 | if (honeycomb && bitmap != options.inBitmap && options.inBitmap != null) {
40 | options.inBitmap.recycle();
41 | options.inBitmap = null;
42 | }
43 |
44 | if (bitmap == null) {
45 | try {
46 | bitmap = BitmapFactory.decodeFile(path, options);
47 | } catch (OutOfMemoryError e) {
48 | e.printStackTrace();
49 | }
50 | }
51 | return bitmap;
52 | }
53 | public static int getSampleSize(String pathName, int reqWidth, int reqHeight) {
54 | // decode bound
55 | int[] bound = decodeBound(pathName);
56 |
57 | // calculate sample size
58 | int sampleSize = SampleSizeUtil.calculateSampleSize(bound[0], bound[1], reqWidth, reqHeight);
59 |
60 | return sampleSize;
61 | }
62 |
63 |
64 | public static int[] decodeBound(String pathName) {
65 | BitmapFactory.Options options = new BitmapFactory.Options();
66 | options.inJustDecodeBounds = true;
67 | BitmapFactory.decodeFile(pathName, options);
68 |
69 | return new int[]{options.outWidth, options.outHeight};
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/widgets/PointIndicator.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.widgets;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.graphics.Paint;
7 | import android.graphics.Path;
8 | import android.util.AttributeSet;
9 | import android.view.View;
10 |
11 | /**
12 | * Created by ljh on 2015/9/10 0010.
13 | */
14 | public class PointIndicator extends View {
15 | private Paint bPaint, fPaint;
16 | private Path bPath,fPath;
17 | private Context mContext;
18 | private int radius;
19 | private int offset;
20 | private int count;
21 | private int position;
22 |
23 | public PointIndicator(Context context) {
24 | super(context);
25 | init(context);
26 |
27 | }
28 |
29 | public void setIndicator(int radius,int offset,int fColor,int bColor ){
30 | this.radius=radius;
31 | this.offset=offset;
32 | bPaint.setColor(bColor);
33 | fPaint.setColor(fColor);
34 |
35 | }
36 |
37 | public void setIndicatorCount(int count){
38 | this.count=count;
39 | for (int i=0;iNUL 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 |
--------------------------------------------------------------------------------
/.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 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 1.8
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/adapter/ExtraActionsPagerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.adapter;
2 |
3 | import android.content.Context;
4 | import android.support.v4.view.PagerAdapter;
5 | import android.support.v4.view.ViewPager;
6 | import android.view.Gravity;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.AdapterView;
10 | import android.widget.GridView;
11 |
12 | import com.saysay.ljh.chattingui.R;
13 | import com.saysay.ljh.chattingui.message.actions.BaseAction;
14 |
15 | import java.util.List;
16 |
17 | /**
18 | * Created by ljh on 2016/10/19.
19 | */
20 |
21 | public class ExtraActionsPagerAdapter extends PagerAdapter {
22 |
23 | private List actions;
24 | private final int ITEM_COUNT_GRID_VIEW = 8;
25 | private Context mContext;
26 | private int pagers;
27 | private ViewPager mViewPager;
28 |
29 |
30 | public ExtraActionsPagerAdapter(Context context, ViewPager viewPager,List baseActions) {
31 | actions=baseActions;
32 | this.mContext = context;
33 | this.mViewPager = viewPager;
34 | this.pagers = (actions.size() + ITEM_COUNT_GRID_VIEW - 1) / ITEM_COUNT_GRID_VIEW;
35 | }
36 |
37 |
38 | @Override
39 | public int getCount() {
40 | return pagers;
41 | }
42 |
43 | @Override
44 | public boolean isViewFromObject(View view, Object object) {
45 | return view == object;
46 | }
47 |
48 | @Override
49 | public Object instantiateItem(ViewGroup container, int position) {
50 | int end = (position + 1) * ITEM_COUNT_GRID_VIEW > actions.size() ? actions
51 | .size() : (position + 1) * ITEM_COUNT_GRID_VIEW;
52 | List subBaseActions = actions.subList(position
53 | * ITEM_COUNT_GRID_VIEW, end);
54 |
55 | GridView gridView = new GridView(mContext);
56 | gridView.setAdapter(new ExtarActionsGridAdapter(mContext, subBaseActions));
57 | gridView.setNumColumns(4);
58 | gridView.setSelector(R.color.transparent);
59 | gridView.setHorizontalSpacing(0);
60 | gridView.setVerticalSpacing(0);
61 | gridView.setGravity(Gravity.CENTER);
62 | gridView.setTag(Integer.valueOf(position));
63 | gridView.setOnItemClickListener(new GridView.OnItemClickListener() {
64 |
65 | @Override
66 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
67 | int index = ((Integer) parent.getTag()) * ITEM_COUNT_GRID_VIEW + position;
68 | actions.get(index).onClick();
69 | }
70 | });
71 |
72 | container.addView(gridView);
73 | return gridView;
74 | }
75 |
76 |
77 | @Override
78 | public int getItemPosition(Object object) {
79 | return POSITION_NONE;
80 | }
81 |
82 | @Override
83 | public void destroyItem(ViewGroup container, int position, Object object) {
84 | // super.destroyItem(container, position, object);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/adapter/MessageAdapter.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.adapter;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.ViewGroup;
7 |
8 | import com.saysay.ljh.chattingui.R;
9 | import com.saysay.ljh.chattingui.message.holder.MessagePictureHolder;
10 | import com.saysay.ljh.chattingui.message.holder.MessageTextHolder;
11 | import com.saysay.ljh.chattingui.message.holder.UnknownHolder;
12 | import com.saysay.ljh.chattingui.message.model.IMessage;
13 | import com.saysay.ljh.chattingui.message.model.MsgTypeEnum;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | /**
19 | * Created by ljh on 2016/10/17.
20 | */
21 |
22 | public class MessageAdapter extends RecyclerView.Adapter {
23 | private Context mContext;
24 | private LayoutInflater mInflater;
25 | private List msgs;
26 |
27 | public MessageAdapter(Context context) {
28 | mContext = context;
29 | mInflater = LayoutInflater.from(context);
30 | msgs = new ArrayList<>();
31 | }
32 |
33 | @Override
34 | public int getItemViewType(int position) {
35 | return msgs.get(position).getType().getValue();
36 | }
37 |
38 | public void setMsgs(List msgs) {
39 | this.msgs = msgs;
40 | notifyDataSetChanged();
41 | }
42 |
43 | @Override
44 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
45 | RecyclerView.ViewHolder holder;
46 | switch (MsgTypeEnum.typeOfValue(viewType)) {
47 | case TEXT:
48 | holder = new MessageTextHolder(mInflater.inflate(R.layout.im_msg_item, parent, false));
49 | break;
50 | case IMAGE:
51 | holder = new MessagePictureHolder(mInflater.inflate(R.layout.im_msg_item, parent, false));
52 | break;
53 | case UNDEF:
54 | holder = new UnknownHolder(mInflater.inflate(R.layout.item_empty, parent, false));
55 | break;
56 | default:
57 | holder = new UnknownHolder(mInflater.inflate(R.layout.item_empty, parent, false));
58 | break;
59 | }
60 |
61 | return holder;
62 | }
63 |
64 | @Override
65 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
66 |
67 | switch (MsgTypeEnum.typeOfValue(getItemViewType(position))) {
68 | case TEXT: {
69 | MessageTextHolder h = (MessageTextHolder) holder;
70 | h.bindMessage(msgs.get(position));
71 | h.showTime(position / 2 == 0);
72 | break;
73 | }
74 | case IMAGE: {
75 | MessagePictureHolder h = (MessagePictureHolder) holder;
76 | h.bindMessage(msgs.get(position));
77 | h.showTime(position / 2 == 0);
78 | break;
79 | }
80 | case UNDEF:
81 | break;
82 | default:
83 | break;
84 | }
85 |
86 | }
87 |
88 | @Override
89 | public int getItemCount() {
90 | return msgs.size();
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/utils/ScreenUtil.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.utils;
2 |
3 | import android.content.Context;
4 | import android.content.res.Resources;
5 | import android.util.DisplayMetrics;
6 | import android.util.Log;
7 |
8 | import java.lang.reflect.Field;
9 |
10 | public class ScreenUtil {
11 | private static final String TAG = "Demo.ScreenUtil";
12 |
13 | private static double RATIO = 0.85;
14 |
15 | public static int screenWidth;
16 | public static int screenHeight;
17 | public static int screenMin;// 宽高中,小的一边
18 | public static int screenMax;// 宽高中,较大的值
19 |
20 | public static float density;
21 | public static float scaleDensity;
22 | public static float xdpi;
23 | public static float ydpi;
24 | public static int densityDpi;
25 |
26 | public static int dialogWidth;
27 | public static int statusbarheight;
28 | public static int navbarheight;
29 |
30 |
31 | public static int dip2px(float dipValue) {
32 | return (int) (dipValue * density + 0.5f);
33 | }
34 |
35 | public static int px2dip(float pxValue) {
36 | return (int) (pxValue / density + 0.5f);
37 | }
38 |
39 | public static int getDialogWidth() {
40 | dialogWidth = (int) (screenMin * RATIO);
41 | return dialogWidth;
42 | }
43 |
44 | public static void init(Context context) {
45 | if (null == context) {
46 | return;
47 | }
48 | DisplayMetrics dm = context.getApplicationContext().getResources().getDisplayMetrics();
49 | screenWidth = dm.widthPixels;
50 | screenHeight = dm.heightPixels;
51 | screenMin = (screenWidth > screenHeight) ? screenHeight : screenWidth;
52 | density = dm.density;
53 | scaleDensity = dm.scaledDensity;
54 | xdpi = dm.xdpi;
55 | ydpi = dm.ydpi;
56 | densityDpi = dm.densityDpi;
57 |
58 | Log.d(TAG, "screenWidth=" + screenWidth + " screenHeight=" + screenHeight + " density=" + density);
59 | }
60 |
61 | public static int getDisplayHeight(Context context) {
62 | if(screenHeight == 0){
63 | GetInfo(context);
64 | }
65 | return screenHeight;
66 | }
67 |
68 | public static void GetInfo(Context context) {
69 | if (null == context) {
70 | return;
71 | }
72 | DisplayMetrics dm = context.getApplicationContext().getResources().getDisplayMetrics();
73 | screenWidth = dm.widthPixels;
74 | screenHeight = dm.heightPixels;
75 | screenMin = (screenWidth > screenHeight) ? screenHeight : screenWidth;
76 | screenMax = (screenWidth < screenHeight) ? screenHeight : screenWidth;
77 | density = dm.density;
78 | scaleDensity = dm.scaledDensity;
79 | xdpi = dm.xdpi;
80 | ydpi = dm.ydpi;
81 | densityDpi = dm.densityDpi;
82 | statusbarheight = getStatusBarHeight(context);
83 | navbarheight = getNavBarHeight(context);
84 | Log.d(TAG, "screenWidth=" + screenWidth + " screenHeight=" + screenHeight + " density=" + density);
85 | }
86 |
87 | public static int getStatusBarHeight(Context context) {
88 | Class> c = null;
89 | Object obj = null;
90 | Field field = null;
91 | int x = 0, sbar = 0;
92 | try {
93 | c = Class.forName("com.android.internal.R$dimen");
94 | obj = c.newInstance();
95 | field = c.getField("status_bar_height");
96 | x = Integer.parseInt(field.get(obj).toString());
97 | sbar = context.getResources().getDimensionPixelSize(x);
98 | } catch (Exception E) {
99 | E.printStackTrace();
100 | }
101 | return sbar;
102 | }
103 |
104 | public static int getNavBarHeight(Context context){
105 | Resources resources = context.getResources();
106 | int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
107 | if (resourceId > 0) {
108 | return resources.getDimensionPixelSize(resourceId);
109 | }
110 | return 0;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/pop_chatting_voice.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
22 |
23 |
28 |
29 |
36 |
37 |
47 |
48 |
49 |
50 |
59 |
60 |
65 |
66 |
77 |
78 |
79 |
80 |
88 |
89 |
93 |
94 |
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/chatting_footer.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
21 |
22 |
29 |
30 |
38 |
39 |
49 |
50 |
64 |
65 |
66 |
76 |
77 |
86 |
87 |
91 |
92 |
93 |
97 |
98 |
104 |
105 |
106 |
111 |
112 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/utils/SampleSizeUtil.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.utils;
2 |
3 | import android.opengl.GLES10;
4 |
5 | /**
6 | * Created by ljh on 2016/10/20.
7 | */
8 |
9 | public class SampleSizeUtil {
10 | public static int calculateSampleSize(String imagePath, int totalPixel) {
11 | int[] bound = BitmapDecoder.decodeBound(imagePath);
12 | return calculateSampleSize(bound[0], bound[1], totalPixel);
13 | }
14 |
15 | public static int calculateSampleSize(int width, int height, int totalPixel) {
16 | int ratio = 1;
17 |
18 | if (width > 0 && height > 0) {
19 | ratio = (int) Math.sqrt((float) (width * height) / totalPixel);
20 | if (ratio < 1) {
21 | ratio = 1;
22 | }
23 | }
24 |
25 | return ratio;
26 | }
27 |
28 | /**
29 | * Calculate an inSampleSize for use in a {@link android.graphics.BitmapFactory.Options}
30 | * object when decoding bitmaps using the decode* methods from
31 | * {@link android.graphics.BitmapFactory}. This implementation calculates the closest
32 | * inSampleSize that will result in the final decoded bitmap having a width
33 | * and height equal to or larger than the requested width and height. This
34 | * implementation does not ensure a power of 2 is returned for inSampleSize
35 | * which can be faster when decoding but results in a larger bitmap which
36 | * isn't as useful for caching purposes.
37 | *
38 | * @param width
39 | * @param height
40 | * @param reqWidth
41 | * @param reqHeight
42 | * @return
43 | */
44 | public static int calculateSampleSize(int width, int height, int reqWidth, int reqHeight) {
45 | // can't proceed
46 | if (width <= 0 || height <= 0) {
47 | return 1;
48 | }
49 | // can't proceed
50 | if (reqWidth <= 0 && reqHeight <= 0) {
51 | return 1;
52 | } else if (reqWidth <= 0) {
53 | reqWidth = (int) (width * reqHeight / (float)height + 0.5f) ;
54 | } else if (reqHeight <= 0) {
55 | reqHeight = (int) (height * reqWidth / (float)width + 0.5f);
56 | }
57 |
58 | int inSampleSize = 1;
59 |
60 | if (height > reqHeight || width > reqWidth) {
61 | // Calculate ratios of height and width to requested height and width
62 | final int heightRatio = Math.round((float) height / (float) reqHeight);
63 | final int widthRatio = Math.round((float) width / (float) reqWidth);
64 |
65 | // Choose the smallest ratio as inSampleSize value, this will
66 | // guarantee a final image
67 | // with both dimensions larger than or equal to the requested height and width.
68 | inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
69 | if (inSampleSize == 0) {
70 | inSampleSize = 1;
71 | }
72 |
73 | // This offers some additional logic in case the image has a strange
74 | // aspect ratio. For example, a panorama may have a much larger
75 | // width than height. In these cases the total pixels might still
76 | // end up being too large to fit comfortably in memory, so we should
77 | // be more aggressive with sample down the image (=larger
78 | // inSampleSize).
79 |
80 | final float totalPixels = width * height;
81 |
82 | // Anything more than 2x the requested pixels we'll sample down
83 | // further
84 | final float totalReqPixelsCap = reqWidth * reqHeight * 2;
85 |
86 | while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
87 | inSampleSize++;
88 | }
89 | }
90 |
91 | return inSampleSize;
92 | }
93 |
94 | public static final int adjustSampleSizeWithTexture(int sampleSize, int width, int height) {
95 | int textureSize = getTextureSize();
96 |
97 | if ((textureSize > 0) && ((width > sampleSize) || (height > sampleSize))) {
98 | while ((width / (float)sampleSize) > textureSize || (height / (float)sampleSize) > textureSize) {
99 | sampleSize++;
100 | }
101 |
102 | // 2的指数对齐
103 | sampleSize = SampleSizeUtil.roundup2n(sampleSize);
104 | }
105 |
106 | return sampleSize;
107 | }
108 |
109 | private static int textureSize = 0;
110 | //存在第二次拿拿不到的情况,所以把拿到的数据用一个static变量保存下来
111 | public static final int getTextureSize() {
112 | if (textureSize > 0) {
113 | return textureSize;
114 | }
115 |
116 | int[] params = new int[1];
117 | GLES10.glGetIntegerv(GLES10.GL_MAX_TEXTURE_SIZE, params, 0);
118 | textureSize = params[0];
119 |
120 | return textureSize;
121 | }
122 |
123 | // 将x向上对齐到2的幂指数
124 | private static final int roundup2n(int x) {
125 | if ((x & (x - 1)) == 0) {
126 | return x;
127 | }
128 | int pos = 0;
129 | while (x > 0) {
130 | x >>= 1;
131 | ++pos;
132 | }
133 | return 1 << pos;
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/message/holder/BaseMessageHolder.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.message.holder;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.Gravity;
5 | import android.view.View;
6 | import android.widget.FrameLayout;
7 | import android.widget.ImageView;
8 | import android.widget.LinearLayout;
9 | import android.widget.ProgressBar;
10 | import android.widget.RelativeLayout;
11 | import android.widget.TextView;
12 |
13 | import com.saysay.ljh.chattingui.R;
14 | import com.saysay.ljh.chattingui.message.model.IMessage;
15 | import com.saysay.ljh.chattingui.message.model.MsgDirectionEnum;
16 | import com.saysay.ljh.chattingui.message.model.MsgStatusEnum;
17 | import com.saysay.ljh.chattingui.utils.TimeUtil;
18 |
19 | /**
20 | * Created by ljh on 2016/10/19.
21 | */
22 |
23 | public class BaseMessageHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
24 | protected ImageView avatarLeft;
25 | protected ImageView avatarRight;
26 | protected TextView msgTime;
27 | protected ImageView msgAlter;
28 | protected ProgressBar msgProgress;
29 | private FrameLayout msgBodyLayout;
30 | protected LinearLayout msgBody;
31 | protected FrameLayout msgContent;
32 |
33 | protected IMessage mMessage;
34 |
35 | private SendMessageFailListener sendMessageFailListener;
36 |
37 | public void setSendMessageFailListener(SendMessageFailListener listener) {
38 | this.sendMessageFailListener = listener;
39 | }
40 |
41 | public SendMessageFailListener getSendMessageFailListener() {
42 | return sendMessageFailListener;
43 | }
44 |
45 | public BaseMessageHolder(View itemView) {
46 | super(itemView);
47 | msgTime = (TextView) itemView.findViewById(R.id.message_item_time);
48 | avatarLeft = (ImageView) itemView.findViewById(R.id.message_item_avatar_left);
49 | avatarRight = (ImageView) itemView.findViewById(R.id.message_item_avatar_right);
50 | msgAlter = (ImageView) itemView.findViewById(R.id.message_item_alert);
51 | msgProgress = (ProgressBar) (itemView.findViewById(R.id.message_item_progress));
52 | msgBodyLayout = (FrameLayout) itemView.findViewById(R.id.msg_body_layout);
53 | msgBody = (LinearLayout) itemView.findViewById(R.id.message_item_body);
54 | msgContent = (FrameLayout) itemView.findViewById(R.id.message_item_content);
55 | msgAlter.setOnClickListener(this);
56 | }
57 |
58 | public void bindMessage(IMessage iMessage) {
59 | mMessage = iMessage;
60 | setContent();
61 | setStatus();
62 | showTime(false);
63 | }
64 |
65 | protected final void setContent() {
66 | int index = (isReceiveMessage() ? 0 : 2);
67 | if (msgBody.getChildAt(index) != msgContent) {
68 | msgBody.removeView(msgContent);
69 | msgBody.addView(msgContent, index);
70 | }
71 | RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) msgBodyLayout.getLayoutParams();
72 | int anchor = (index == 2 ? avatarRight.getId() : avatarLeft.getId());
73 | lp.addRule(RelativeLayout.ALIGN_TOP, anchor);
74 | if (isReceiveMessage()) {
75 | msgBody.setGravity(Gravity.LEFT);
76 | msgContent.setBackgroundResource(leftBackground());
77 | avatarLeft.setVisibility(View.VISIBLE);
78 | avatarRight.setVisibility(View.GONE);
79 | } else {
80 | msgBody.setGravity(Gravity.RIGHT);
81 | msgContent.setBackgroundResource(rightBackground());
82 | avatarLeft.setVisibility(View.GONE);
83 | avatarRight.setVisibility(View.VISIBLE);
84 | }
85 | }
86 |
87 | protected int leftBackground() {
88 | return R.drawable.im_msg_text_receive_bg_sel;
89 | }
90 |
91 | protected int rightBackground() {
92 | return R.drawable.im_msg_text_send_bg_sel;
93 | }
94 |
95 | protected final boolean isReceiveMessage() {
96 | return MsgDirectionEnum.RECEIVE == mMessage.getDirection();
97 | }
98 |
99 |
100 | /**
101 | * 设置消息发送状态
102 | */
103 | private void setStatus() {
104 | MsgStatusEnum status = mMessage.getStatus();
105 | switch (status) {
106 | case FAIL:
107 | msgProgress.setVisibility(View.GONE);
108 | msgAlter.setVisibility(View.VISIBLE);
109 | break;
110 | case SENDING:
111 | msgProgress.setVisibility(View.VISIBLE);
112 | msgAlter.setVisibility(View.GONE);
113 | break;
114 | default:
115 | msgProgress.setVisibility(View.GONE);
116 | msgAlter.setVisibility(View.GONE);
117 | break;
118 | }
119 | }
120 |
121 | /**
122 | * 设置时间显示
123 | */
124 | public void showTime(boolean show) {
125 | if (show) {
126 | msgTime.setVisibility(View.VISIBLE);
127 | } else {
128 | msgTime.setVisibility(View.GONE);
129 | return;
130 | }
131 | String text = TimeUtil.getTimeShowString(mMessage.getTime(), false);
132 | msgTime.setText(text);
133 | }
134 |
135 | @Override
136 | public void onClick(View v) {
137 | if (null != sendMessageFailListener) {
138 | sendMessageFailListener.sendFail(mMessage);
139 | }
140 | }
141 |
142 | public interface SendMessageFailListener {
143 | public void sendFail(IMessage iMessage);
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/im_msg_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
25 |
26 |
35 |
36 |
45 |
46 |
54 |
55 |
64 |
65 |
80 |
81 |
82 |
88 |
89 |
99 |
100 |
110 |
111 |
120 |
121 |
127 |
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/ChattingActivity.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.os.IBinder;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.support.v7.widget.LinearLayoutManager;
8 | import android.support.v7.widget.RecyclerView;
9 | import android.view.GestureDetector;
10 | import android.view.MotionEvent;
11 | import android.view.View;
12 | import android.view.inputmethod.InputMethodManager;
13 |
14 | import com.saysay.ljh.chattingui.message.adapter.MessageAdapter;
15 | import com.saysay.ljh.chattingui.widgets.ChattingFooter;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 |
20 | /**
21 | * Created by ljh on 2016/1/26.
22 | */
23 | public class ChattingActivity extends AppCompatActivity {
24 | private ChattingFooter mChattingFooter;
25 | private View mRoot;
26 | private RecyclerView rlvMessage;
27 | private MessageAdapter messageAdapter;
28 | private List msgs = new ArrayList<>();
29 | private GestureDetector detector;
30 | private GestureDetector.SimpleOnGestureListener simpleOnGestureListener = new GestureDetector.SimpleOnGestureListener() {
31 | @Override
32 | public boolean onDown(MotionEvent e) {
33 | if (mChattingFooter.isShowExtra()) {
34 | mChattingFooter.hideExtra();
35 | }
36 | hideSoftKeyboard();
37 | return true;
38 | }
39 |
40 | @Override
41 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
42 | if (mChattingFooter.isShowExtra()) {
43 | mChattingFooter.hideExtra();
44 | }
45 | hideSoftKeyboard();
46 | return super.onFling(e1, e2, velocityX, velocityY);
47 | }
48 | };
49 |
50 | @Override
51 | public void onBackPressed() {
52 | if (mChattingFooter.isShowExtra()) {
53 | mChattingFooter.hideExtra();
54 | return;
55 | }
56 | super.onBackPressed();
57 | }
58 |
59 | @Override
60 | protected void onCreate(Bundle savedInstanceState) {
61 | super.onCreate(savedInstanceState);
62 | setContentView(R.layout.activity_chatting);
63 | mChattingFooter = (ChattingFooter) findViewById(R.id.chatting_footer);
64 | mRoot = findViewById(R.id.ll_root);
65 | detector = new GestureDetector(this, simpleOnGestureListener);
66 | mChattingFooter.setActivity(this, mRoot);
67 | rlvMessage = (RecyclerView) findViewById(R.id.rlv_message);
68 | rlvMessage.setHasFixedSize(true);
69 | LinearLayoutManager llm = new LinearLayoutManager(this);
70 | rlvMessage.setLayoutManager(llm);
71 | messageAdapter = new MessageAdapter(this);
72 | rlvMessage.setAdapter(messageAdapter);
73 | rlvMessage.setOverScrollMode(View.OVER_SCROLL_NEVER);
74 | mChattingFooter.setOnChattingFooterListener(new ChattingFooter.OnChattingFooterListener() {
75 | @Override
76 | public void OnVoiceRcdInitReuqest() {
77 |
78 | }
79 |
80 | @Override
81 | public void OnVoiceRcdStartRequest() {
82 |
83 | }
84 |
85 | @Override
86 | public void OnVoiceRcdCancelRequest() {
87 |
88 | }
89 |
90 | @Override
91 | public void OnVoiceRcdStopRequest() {
92 |
93 | }
94 |
95 | @Override
96 | public void OnSendTextMessageRequest(CharSequence text) {
97 | }
98 |
99 | @Override
100 | public void OnUpdateTextOutBoxRequest(CharSequence text) {
101 |
102 | }
103 |
104 | @Override
105 | public void OnSendCustomEmojiRequest(int emojiid, String emojiName) {
106 |
107 | }
108 |
109 | @Override
110 | public void OnEmojiDelRequest() {
111 |
112 | }
113 |
114 | @Override
115 | public void OnInEditMode() {
116 | rlvMessage.postDelayed(new Runnable() {
117 | @Override
118 | public void run() {
119 | rlvMessage.scrollToPosition(msgs.size() - 1);
120 | }
121 | }, 200);
122 |
123 | }
124 |
125 | @Override
126 | public void onPause() {
127 |
128 | }
129 |
130 | @Override
131 | public void onResume() {
132 |
133 | }
134 |
135 | @Override
136 | public void release() {
137 |
138 | }
139 | });
140 |
141 | rlvMessage.setOnTouchListener(new View.OnTouchListener() {
142 | @Override
143 | public boolean onTouch(View v, MotionEvent event) {
144 | detector.onTouchEvent(event);
145 | return false;
146 | }
147 | });
148 | }
149 |
150 |
151 | @Override
152 | protected void onPostCreate(Bundle savedInstanceState) {
153 | super.onPostCreate(savedInstanceState);
154 | }
155 |
156 | // @Override
157 | // public boolean onKeyDown(int keyCode, KeyEvent event) {
158 | // boolean b = false;
159 | // if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
160 | // if (mChattingFooter.isShowExtra()) {
161 | // mChattingFooter.hideExtra();
162 | // b = true;
163 | // }
164 | // b = hideSoftKeyboard() || b;
165 | // if (b) {
166 | // return true;
167 | // } else {
168 | // onBackPressed();
169 | // }
170 | // return true;
171 | // }
172 | // return super.onKeyDown(keyCode, event);
173 | // }
174 |
175 | /**
176 | * hide inputMethod
177 | */
178 | public boolean hideSoftKeyboard() {
179 | boolean b = false;
180 | InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
181 | if (inputMethodManager != null) {
182 | View localView = getCurrentFocus();
183 | if (localView != null && localView.getWindowToken() != null) {
184 | IBinder windowToken = localView.getWindowToken();
185 | b = inputMethodManager.hideSoftInputFromWindow(windowToken, 0);
186 |
187 | }
188 | }
189 | return b;
190 | }
191 |
192 | }
193 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/ChattingFragment.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.os.IBinder;
6 | import android.support.annotation.Nullable;
7 | import android.support.v4.app.Fragment;
8 | import android.support.v7.widget.LinearLayoutManager;
9 | import android.support.v7.widget.RecyclerView;
10 | import android.util.Log;
11 | import android.view.GestureDetector;
12 | import android.view.LayoutInflater;
13 | import android.view.MotionEvent;
14 | import android.view.View;
15 | import android.view.ViewGroup;
16 | import android.view.inputmethod.InputMethodManager;
17 |
18 | import com.saysay.ljh.chattingui.message.adapter.MessageAdapter;
19 | import com.saysay.ljh.chattingui.message.model.Container;
20 | import com.saysay.ljh.chattingui.message.model.IMessage;
21 | import com.saysay.ljh.chattingui.message.model.MessageTextBody;
22 | import com.saysay.ljh.chattingui.message.model.ModuleProxy;
23 | import com.saysay.ljh.chattingui.message.model.MsgDirectionEnum;
24 | import com.saysay.ljh.chattingui.message.model.MsgStatusEnum;
25 | import com.saysay.ljh.chattingui.message.model.MsgTypeEnum;
26 | import com.saysay.ljh.chattingui.message.model.SimpleMessage;
27 | import com.saysay.ljh.chattingui.widgets.ChattingFooter;
28 |
29 | import java.util.ArrayList;
30 | import java.util.Calendar;
31 | import java.util.List;
32 |
33 | /**
34 | * Created by ljh on 2016/10/18.
35 | */
36 |
37 | public class ChattingFragment extends Fragment implements ChattingFooter.OnChattingFooterListener, ModuleProxy {
38 |
39 | private ChattingFooter mChattingFooter;
40 | private View mRoot;
41 | private RecyclerView rlvMessage;
42 | private MessageAdapter messageAdapter;
43 | private List msgs = new ArrayList<>();
44 | private GestureDetector detector;
45 |
46 | private GestureDetector.SimpleOnGestureListener simpleOnGestureListener = new GestureDetector.SimpleOnGestureListener() {
47 | @Override
48 | public boolean onDown(MotionEvent e) {
49 | if (mChattingFooter.isShowExtra()) {
50 | mChattingFooter.hideExtra();
51 | }
52 | hideSoftKeyboard();
53 | return true;
54 | }
55 |
56 | @Override
57 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
58 | if (mChattingFooter.isShowExtra()) {
59 | mChattingFooter.hideExtra();
60 | }
61 | hideSoftKeyboard();
62 | return super.onFling(e1, e2, velocityX, velocityY);
63 | }
64 | };
65 |
66 | public static ChattingFragment newFragment() {
67 | return new ChattingFragment();
68 | }
69 |
70 | @Nullable
71 | @Override
72 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
73 | View rootView = inflater.inflate(R.layout.fragment_chatting, container, false);
74 | initView(rootView);
75 | return rootView;
76 | }
77 |
78 | private void initView(View rootView) {
79 | mChattingFooter = (ChattingFooter) rootView.findViewById(R.id.chatting_footer);
80 | mRoot = rootView.findViewById(R.id.ll_root);
81 | detector = new GestureDetector(getContext(), simpleOnGestureListener);
82 | mChattingFooter.setActivity(getActivity(), mRoot);
83 | rlvMessage = (RecyclerView) rootView.findViewById(R.id.rlv_message);
84 | rlvMessage.setHasFixedSize(true);
85 | LinearLayoutManager llm = new LinearLayoutManager(getContext());
86 | rlvMessage.setLayoutManager(llm);
87 | messageAdapter = new MessageAdapter(getContext());
88 | rlvMessage.setAdapter(messageAdapter);
89 | rlvMessage.setOverScrollMode(View.OVER_SCROLL_NEVER);
90 | mChattingFooter.setOnChattingFooterListener(this);
91 | rlvMessage.setOnTouchListener(new View.OnTouchListener() {
92 | @Override
93 | public boolean onTouch(View v, MotionEvent event) {
94 | detector.onTouchEvent(event);
95 | return false;
96 | }
97 | });
98 |
99 | Container container = new Container(getActivity(), "ssss", this);
100 | mChattingFooter.setContainer(container);
101 | }
102 |
103 | @Override
104 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
105 | super.onViewCreated(view, savedInstanceState);
106 | }
107 |
108 | @Override
109 | public void OnVoiceRcdInitReuqest() {
110 |
111 | }
112 |
113 | @Override
114 | public void OnVoiceRcdStartRequest() {
115 |
116 | }
117 |
118 | @Override
119 | public void OnVoiceRcdCancelRequest() {
120 |
121 | }
122 |
123 | @Override
124 | public void OnVoiceRcdStopRequest() {
125 |
126 | }
127 |
128 | int position = 0;
129 | // static List urls = new ArrayList<>();
130 | //
131 | // static {
132 | // urls.add("/storage/emulated/0/1yibao/a.jpg");
133 | // urls.add("/storage/emulated/0/1yibao/b.jpg");
134 | // urls.add("/storage/emulated/0/1yibao/c.jpg");
135 | // urls.add("/storage/emulated/0/1yibao/d.jpg");
136 | // }
137 |
138 | @Override
139 | public void OnSendTextMessageRequest(CharSequence text) {
140 | SimpleMessage simpleMessage = new SimpleMessage();
141 |
142 | simpleMessage.setType(MsgTypeEnum.TEXT);
143 |
144 | if (position % 2 == 0) {
145 | simpleMessage.setDirection(MsgDirectionEnum.SEND);
146 | } else {
147 | simpleMessage.setDirection(MsgDirectionEnum.RECEIVE);
148 | }
149 | if (position % 3 == 0) {
150 | simpleMessage.setStatus(MsgStatusEnum.SUCCESS);
151 | } else {
152 | simpleMessage.setStatus(MsgStatusEnum.FAIL);
153 | }
154 | simpleMessage.setTime(Calendar.getInstance().getTimeInMillis());
155 | MessageTextBody messageText = new MessageTextBody();
156 | messageText.setContent(text.toString());
157 | simpleMessage.setMessageBody(messageText);
158 |
159 | // MessagePictureBody messagePictureBody = new MessagePictureBody();
160 | // messagePictureBody.setPath(urls.get(position));
161 | // simpleMessage.setMessageBody(messagePictureBody);
162 | msgs.add(simpleMessage);
163 | messageAdapter.setMsgs(msgs);
164 | rlvMessage.postDelayed(new Runnable() {
165 | @Override
166 | public void run() {
167 | rlvMessage.scrollToPosition(msgs.size() - 1);
168 | }
169 | }, 100);
170 | position++;
171 | }
172 |
173 | @Override
174 | public void OnUpdateTextOutBoxRequest(CharSequence text) {
175 |
176 | }
177 |
178 | @Override
179 | public void OnSendCustomEmojiRequest(int emojiid, String emojiName) {
180 |
181 | }
182 |
183 | @Override
184 | public void OnEmojiDelRequest() {
185 |
186 | }
187 |
188 | @Override
189 | public void OnInEditMode() {
190 | rlvMessage.postDelayed(new Runnable() {
191 | @Override
192 | public void run() {
193 | rlvMessage.scrollToPosition(msgs.size() - 1);
194 | }
195 | }, 200);
196 | }
197 |
198 | @Override
199 | public void release() {
200 |
201 | }
202 |
203 |
204 | /**
205 | * hide inputMethod
206 | */
207 | public boolean hideSoftKeyboard() {
208 | boolean b = false;
209 | InputMethodManager inputMethodManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
210 | if (inputMethodManager != null) {
211 | View localView = getActivity().getCurrentFocus();
212 | if (localView != null && localView.getWindowToken() != null) {
213 | IBinder windowToken = localView.getWindowToken();
214 | b = inputMethodManager.hideSoftInputFromWindow(windowToken, 0);
215 |
216 | }
217 | }
218 | return b;
219 | }
220 |
221 | @Override
222 | public void sendMessage(IMessage message) {
223 | Log.d("tag", "sendMessage");
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/utils/TimeUtil.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.utils;
2 |
3 | import java.math.BigDecimal;
4 | import java.text.ParseException;
5 | import java.text.SimpleDateFormat;
6 | import java.util.Calendar;
7 | import java.util.Date;
8 | import java.util.GregorianCalendar;
9 | import java.util.Locale;
10 | import java.util.TimeZone;
11 |
12 | public class TimeUtil {
13 |
14 | public static boolean isEarly(int days, long time) {
15 | return (currentTimeMillis() - time) > (days * 24 * 3600 * 1000);
16 | }
17 |
18 | public static int currentTimeSecond() {
19 | return (int) (System.currentTimeMillis() / 1000);
20 | }
21 |
22 | public static long currentTimeMillis() {
23 | return System.currentTimeMillis();
24 | }
25 |
26 | public static long[] getTsTimes() {
27 | long[] times = new long[2];
28 |
29 | Calendar calendar = Calendar.getInstance();
30 |
31 | times[0] = calendar.getTimeInMillis() / 1000;
32 |
33 | calendar.set(Calendar.HOUR_OF_DAY, 0);
34 | calendar.set(Calendar.MINUTE, 0);
35 | calendar.set(Calendar.SECOND, 0);
36 |
37 | times[1] = calendar.getTimeInMillis() / 1000;
38 |
39 | return times;
40 | }
41 |
42 | public static String getFormatDatetime(int year, int month, int day) {
43 | SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
44 | return formatter.format(new GregorianCalendar(year, month, day).getTime());
45 | }
46 |
47 | public static Date getDateFromFormatString(String formatDate) {
48 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
49 | try {
50 | return sdf.parse(formatDate);
51 | } catch (ParseException e) {
52 | e.printStackTrace();
53 | }
54 |
55 | return null;
56 | }
57 |
58 | public static String getNowDatetime() {
59 | SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
60 | return (formatter.format(new Date()));
61 | }
62 |
63 | public static int getNow() {
64 | return (int) ((new Date()).getTime() / 1000);
65 | }
66 |
67 | public static String getNowDateTime(String format) {
68 | Date date = new Date();
69 |
70 | SimpleDateFormat df = new SimpleDateFormat(format, Locale.getDefault());
71 | return df.format(date);
72 | }
73 |
74 | public static String getDateString(long milliseconds) {
75 | return getDateTimeString(milliseconds, "yyyyMMdd");
76 | }
77 |
78 | public static String getTimeString(long milliseconds) {
79 | return getDateTimeString(milliseconds, "HHmmss");
80 | }
81 |
82 | public static String getBeijingNowTimeString(String format) {
83 | TimeZone timezone = TimeZone.getTimeZone("Asia/Shanghai");
84 |
85 | Date date = new Date(currentTimeMillis());
86 | SimpleDateFormat formatter = new SimpleDateFormat(format, Locale.getDefault());
87 | formatter.setTimeZone(timezone);
88 |
89 | GregorianCalendar gregorianCalendar = new GregorianCalendar();
90 | gregorianCalendar.setTimeZone(timezone);
91 | String prefix = gregorianCalendar.get(Calendar.AM_PM) == Calendar.AM ? "上午" : "下午";
92 |
93 | return prefix + formatter.format(date);
94 | }
95 |
96 | public static String getBeijingNowTime(String format) {
97 | TimeZone timezone = TimeZone.getTimeZone("Asia/Shanghai");
98 |
99 | Date date = new Date(currentTimeMillis());
100 | SimpleDateFormat formatter = new SimpleDateFormat(format, Locale.getDefault());
101 | formatter.setTimeZone(timezone);
102 |
103 | return formatter.format(date);
104 | }
105 |
106 | public static String getDateTimeString(long milliseconds, String format) {
107 | Date date = new Date(milliseconds);
108 | SimpleDateFormat formatter = new SimpleDateFormat(format, Locale.getDefault());
109 | return formatter.format(date);
110 | }
111 |
112 |
113 | public static String getFavoriteCollectTime(long milliseconds) {
114 | String showDataString = "";
115 | Date today = new Date();
116 | Date date = new Date(milliseconds);
117 | Date firstDateThisYear = new Date(today.getYear(), 0, 0);
118 | if (!date.before(firstDateThisYear)) {
119 | SimpleDateFormat dateformatter = new SimpleDateFormat("MM-dd", Locale.getDefault());
120 | showDataString = dateformatter.format(date);
121 | } else {
122 | SimpleDateFormat dateformatter = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
123 | showDataString = dateformatter.format(date);
124 | }
125 | return showDataString;
126 | }
127 |
128 | public static String getTimeShowString(long milliseconds, boolean abbreviate) {
129 | String dataString = "";
130 | String timeStringBy24 = "";
131 |
132 | Date currentTime = new Date(milliseconds);
133 | Date today = new Date();
134 | Calendar todayStart = Calendar.getInstance();
135 | todayStart.set(Calendar.HOUR_OF_DAY, 0);
136 | todayStart.set(Calendar.MINUTE, 0);
137 | todayStart.set(Calendar.SECOND, 0);
138 | todayStart.set(Calendar.MILLISECOND, 0);
139 | Date todaybegin = todayStart.getTime();
140 | Date yesterdaybegin = new Date(todaybegin.getTime() - 3600 * 24 * 1000);
141 | Date preyesterday = new Date(yesterdaybegin.getTime() - 3600 * 24 * 1000);
142 |
143 | if (!currentTime.before(todaybegin)) {
144 | dataString = "今天";
145 | } else if (!currentTime.before(yesterdaybegin)) {
146 | dataString = "昨天";
147 | } else if (!currentTime.before(preyesterday)) {
148 | dataString = "前天";
149 | } else if (isSameWeekDates(currentTime, today)) {
150 | dataString = getWeekOfDate(currentTime);
151 | } else {
152 | SimpleDateFormat dateformatter = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
153 | dataString = dateformatter.format(currentTime);
154 | }
155 |
156 | SimpleDateFormat timeformatter24 = new SimpleDateFormat("HH:mm", Locale.getDefault());
157 | timeStringBy24 = timeformatter24.format(currentTime);
158 |
159 | if (abbreviate) {
160 | if (!currentTime.before(todaybegin)) {
161 | return getTodayTimeBucket(currentTime);
162 | } else {
163 | return dataString;
164 | }
165 | } else {
166 | return dataString + " " + timeStringBy24;
167 | }
168 | }
169 |
170 | /**
171 | * 根据不同时间段,显示不同时间
172 | *
173 | * @param date
174 | * @return
175 | */
176 | public static String getTodayTimeBucket(Date date) {
177 | Calendar calendar = Calendar.getInstance();
178 | calendar.setTime(date);
179 | SimpleDateFormat timeformatter0to11 = new SimpleDateFormat("KK:mm", Locale.getDefault());
180 | SimpleDateFormat timeformatter1to12 = new SimpleDateFormat("hh:mm", Locale.getDefault());
181 | int hour = calendar.get(Calendar.HOUR_OF_DAY);
182 | if (hour >= 0 && hour < 5) {
183 | return "凌晨 " + timeformatter0to11.format(date);
184 | } else if (hour >= 5 && hour < 12) {
185 | return "上午 " + timeformatter0to11.format(date);
186 | } else if (hour >= 12 && hour < 18) {
187 | return "下午 " + timeformatter1to12.format(date);
188 | } else if (hour >= 18 && hour < 24) {
189 | return "晚上 " + timeformatter1to12.format(date);
190 | }
191 | return "";
192 | }
193 |
194 | /**
195 | * 根据日期获得星期
196 | *
197 | * @param date
198 | * @return
199 | */
200 | public static String getWeekOfDate(Date date) {
201 | String[] weekDaysName = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
202 | // String[] weekDaysCode = { "0", "1", "2", "3", "4", "5", "6" };
203 | Calendar calendar = Calendar.getInstance();
204 | calendar.setTime(date);
205 | int intWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1;
206 | return weekDaysName[intWeek];
207 | }
208 |
209 | public static boolean isSameDay(long time1, long time2) {
210 | return isSameDay(new Date(time1), new Date(time2));
211 | }
212 |
213 | public static boolean isSameDay(Date date1, Date date2) {
214 | Calendar cal1 = Calendar.getInstance();
215 | Calendar cal2 = Calendar.getInstance();
216 | cal1.setTime(date1);
217 | cal2.setTime(date2);
218 |
219 | boolean sameDay = cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) &&
220 | cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR);
221 | return sameDay;
222 | }
223 |
224 | /**
225 | * 判断两个日期是否在同一周
226 | *
227 | * @param date1
228 | * @param date2
229 | * @return
230 | */
231 | public static boolean isSameWeekDates(Date date1, Date date2) {
232 | Calendar cal1 = Calendar.getInstance();
233 | Calendar cal2 = Calendar.getInstance();
234 | cal1.setTime(date1);
235 | cal2.setTime(date2);
236 | int subYear = cal1.get(Calendar.YEAR) - cal2.get(Calendar.YEAR);
237 | if (0 == subYear) {
238 | if (cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR))
239 | return true;
240 | } else if (1 == subYear && 11 == cal2.get(Calendar.MONTH)) {
241 | // 如果12月的最后一周横跨来年第一周的话则最后一周即算做来年的第一周
242 | if (cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR))
243 | return true;
244 | } else if (-1 == subYear && 11 == cal1.get(Calendar.MONTH)) {
245 | if (cal1.get(Calendar.WEEK_OF_YEAR) == cal2.get(Calendar.WEEK_OF_YEAR))
246 | return true;
247 | }
248 | return false;
249 | }
250 |
251 | public static long getSecondsByMilliseconds(long milliseconds) {
252 | long seconds = new BigDecimal((float) ((float) milliseconds / (float) 1000)).setScale(0,
253 | BigDecimal.ROUND_HALF_UP).intValue();
254 | // if (seconds == 0) {
255 | // seconds = 1;
256 | // }
257 | return seconds;
258 | }
259 |
260 | public static String secToTime(int time) {
261 | String timeStr = null;
262 | int hour = 0;
263 | int minute = 0;
264 | int second = 0;
265 | if (time <= 0)
266 | return "00:00";
267 | else {
268 | minute = time / 60;
269 | if (minute < 60) {
270 | second = time % 60;
271 | timeStr = unitFormat(minute) + ":" + unitFormat(second);
272 | } else {
273 | hour = minute / 60;
274 | if (hour > 99)
275 | return "99:59:59";
276 | minute = minute % 60;
277 | second = time - hour * 3600 - minute * 60;
278 | timeStr = unitFormat(hour) + ":" + unitFormat(minute) + ":" + unitFormat(second);
279 | }
280 | }
281 | return timeStr;
282 | }
283 |
284 | public static String unitFormat(int i) {
285 | String retStr = null;
286 | if (i >= 0 && i < 10)
287 | retStr = "0" + Integer.toString(i);
288 | else retStr = "" + i;
289 | return retStr;
290 | }
291 |
292 | public static String getElapseTimeForShow(int milliseconds) {
293 | StringBuilder sb = new StringBuilder();
294 | int seconds = milliseconds / 1000;
295 | if (seconds < 1)
296 | seconds = 1;
297 | int hour = seconds / (60 * 60);
298 | if (hour != 0) {
299 | sb.append(hour).append("小时");
300 | }
301 | int minute = (seconds - 60 * 60 * hour) / 60;
302 | if (minute != 0) {
303 | sb.append(minute).append("分");
304 | }
305 | int second = (seconds - 60 * 60 * hour - 60 * minute);
306 | if (second != 0) {
307 | sb.append(second).append("秒");
308 | }
309 | return sb.toString();
310 | }
311 | }
312 |
--------------------------------------------------------------------------------
/app/src/main/java/com/saysay/ljh/chattingui/widgets/ChattingFooter.java:
--------------------------------------------------------------------------------
1 | package com.saysay.ljh.chattingui.widgets;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.graphics.Color;
6 | import android.graphics.Rect;
7 | import android.graphics.drawable.ColorDrawable;
8 | import android.os.Environment;
9 | import android.os.StatFs;
10 | import android.support.v4.view.ViewPager;
11 | import android.text.Editable;
12 | import android.text.TextUtils;
13 | import android.text.TextWatcher;
14 | import android.util.AttributeSet;
15 | import android.util.Log;
16 | import android.view.Gravity;
17 | import android.view.LayoutInflater;
18 | import android.view.MotionEvent;
19 | import android.view.View;
20 | import android.view.ViewGroup;
21 | import android.view.ViewTreeObserver;
22 | import android.view.WindowManager;
23 | import android.view.inputmethod.InputMethodManager;
24 | import android.widget.Button;
25 | import android.widget.ImageView;
26 | import android.widget.LinearLayout;
27 | import android.widget.PopupWindow;
28 | import android.widget.Toast;
29 |
30 | import com.saysay.ljh.chattingui.R;
31 | import com.saysay.ljh.chattingui.message.actions.BaseAction;
32 | import com.saysay.ljh.chattingui.message.actions.PickImageAction;
33 | import com.saysay.ljh.chattingui.message.adapter.ExtraActionsPagerAdapter;
34 | import com.saysay.ljh.chattingui.message.model.Container;
35 | import com.saysay.ljh.chattingui.utils.PrefUtils;
36 |
37 | import java.io.File;
38 | import java.util.ArrayList;
39 | import java.util.List;
40 |
41 | /**
42 | * Created by ljh on 2016/1/26.
43 | */
44 | public class ChattingFooter extends LinearLayout implements View.OnClickListener, TextWatcher, ViewTreeObserver.OnGlobalLayoutListener, View.OnTouchListener {
45 | // cancel recording sliding distance field.
46 | private static final int CANCEL_DISTANCE = 60;
47 | private Context mContext;
48 | private View parentRoot, view, vLine;
49 | private EditTextPreIme etInput;
50 | private ImageView ivVoice, ivExtra, ivEmoji;
51 | private ViewPager vpExtra;
52 | private Button btnSend, btnVoice;
53 | private Activity mActivity;
54 | private int keyboardHeight;
55 | private int extraBarHeight;
56 | private LinearLayout.LayoutParams extraLp;
57 | private LayoutInflater mInflater;
58 | private OnChattingFooterListener mChattingFooterListener;
59 | private View extraView;
60 | private ExtraActionsPagerAdapter pagerAdapter;
61 | private boolean deviceHasNavigationBar;
62 | private PointIndicator pointIndicator;
63 |
64 | private Container mContainer;
65 |
66 | public ChattingFooter(Context context) {
67 | super(context);
68 | initView(context);
69 |
70 |
71 | }
72 |
73 | public void setOnChattingFooterListener(OnChattingFooterListener onChattingFooterListener) {
74 | this.mChattingFooterListener = onChattingFooterListener;
75 | }
76 |
77 | public ChattingFooter(Context context, AttributeSet attrs) {
78 | super(context, attrs);
79 | initView(context);
80 | }
81 |
82 | public void setActivity(Activity activity, View root) {
83 | mActivity = activity;
84 | parentRoot = activity.getWindow().getDecorView();
85 | parentRoot.getViewTreeObserver().addOnGlobalLayoutListener(this);
86 | }
87 |
88 | PopupWindow popupWindow;
89 |
90 | public void setContainer(Container mContainer) {
91 | this.mContainer = mContainer;
92 | List baseActions = new ArrayList<>();
93 | for (int i = 0; i < 30; i++) {
94 | PickImageAction pickImageAction = new PickImageAction(R.mipmap.ic_launcher, R.string.app_name);
95 | pickImageAction.setContainer(mContainer);
96 | baseActions.add(pickImageAction);
97 | }
98 | pagerAdapter = new ExtraActionsPagerAdapter(mContext, vpExtra, baseActions);
99 | }
100 |
101 | private void initView(Context context) {
102 | mContext = context;
103 | deviceHasNavigationBar=checkDeviceHasNavigationBar(context);
104 | setRecordPopWindow(context);
105 | setFocusable(true);
106 | keyboardHeight = PrefUtils.getKeyboardHeight(context);
107 | setOrientation(VERTICAL);
108 | view = LayoutInflater.from(mContext).inflate(R.layout.chatting_footer, this, true);
109 | extraView = view.findViewById(R.id.extra_view);
110 | pointIndicator = (PointIndicator) view.findViewById(R.id.pointIndicator);
111 | pointIndicator.setIndicator(10, 30, Color.RED, Color.BLACK);
112 |
113 | etInput = (EditTextPreIme) view.findViewById(R.id.et_input);
114 | ivVoice = (ImageView) view.findViewById(R.id.iv_voice);
115 | ivEmoji = (ImageView) view.findViewById(R.id.iv_emoji);
116 | ivExtra = (ImageView) view.findViewById(R.id.iv_extra);
117 | vpExtra = (ViewPager) view.findViewById(R.id.vp_extra);
118 | btnSend = (Button) view.findViewById(R.id.btn_send);
119 | btnVoice = (Button) view.findViewById(R.id.btn_voice);
120 | vLine = view.findViewById(R.id.v_line);
121 | etInput.setOnClickListener(this);
122 | ivVoice.setOnClickListener(this);
123 | ivEmoji.setOnClickListener(this);
124 | ivExtra.setOnClickListener(this);
125 | btnSend.setOnClickListener(this);
126 | btnVoice.setOnClickListener(this);
127 | etInput.addTextChangedListener(this);
128 | ivVoice.setSelected(false);
129 | ivEmoji.setSelected(false);
130 | extraLp = (LayoutParams) extraView.getLayoutParams();
131 | extraLp.height = keyboardHeight < 200 ? 831 : keyboardHeight;
132 | extraLp.weight = ViewGroup.LayoutParams.MATCH_PARENT;
133 | extraView.setLayoutParams(extraLp);
134 | btnVoice.setOnTouchListener(this);
135 | etInput.setOnKeyBackListener(new EditTextPreIme.OnKeyBackListener() {
136 | @Override
137 | public boolean onKeyBack() {
138 | if (isShowExtra()) {
139 | hideExtra();
140 | hideSoftInputFromWindow(etInput);
141 | ivEmoji.setSelected(false);
142 | return true;
143 | }
144 | return false;
145 | }
146 | });
147 | etInput.requestFocus();
148 | etInput.setCursorVisible(false);
149 |
150 | vpExtra.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
151 | @Override
152 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
153 |
154 | }
155 |
156 | @Override
157 | public void onPageSelected(int position) {
158 |
159 | pointIndicator.translationPoint(position);
160 | }
161 |
162 | @Override
163 | public void onPageScrollStateChanged(int state) {
164 |
165 | }
166 | });
167 | }
168 |
169 | private ImageView mVoiceHintAnim;
170 | private View mVoiceHintAnimArea, mVoiceRcdHitCancelView, mVoiceHintTooshort;
171 |
172 | private void setRecordPopWindow(Context context) {
173 | mInflater = LayoutInflater.from(context);
174 | View recordView = mInflater.inflate(R.layout.pop_chatting_voice, null);
175 | popupWindow = new PopupWindow(recordView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
176 | popupWindow.setBackgroundDrawable(new ColorDrawable(0));
177 | mVoiceHintAnim = (ImageView) recordView.findViewById(R.id.voice_rcd_hint_anim);
178 | mVoiceHintAnimArea = recordView.findViewById(R.id.voice_rcd_hint_anim_area);
179 | mVoiceRcdHitCancelView = recordView.findViewById(R.id.voice_rcd_hint_cancel_area);
180 | // mVoiceHintCancelText = (TextView) recordView.findViewById(R.id.voice_rcd_hint_cancel_text);
181 | // mVoiceHintCancelIcon = (ImageView) popupWindow.getContentView().findViewById(R.id.voice_rcd_hint_cancel_icon);
182 | // mVoiceHintRcding = recordView.findViewById(R.id.voice_rcd_hint_rcding);
183 | mVoiceHintTooshort = recordView.findViewById(R.id.voice_rcd_hint_tooshort);
184 | // mVoiceNormalWording = (TextView) recordView.findViewById(R.id.voice_rcd_normal_wording);
185 |
186 | }
187 |
188 | @Override
189 | public void onClick(View v) {
190 | switch (v.getId()) {
191 | case R.id.et_input:
192 | etInput.setCursorVisible(true);
193 | showSoftInputFromWindow(etInput);
194 | ivEmoji.setSelected(false);
195 | if (null != mChattingFooterListener) {
196 | mChattingFooterListener.OnInEditMode();
197 | }
198 | break;
199 | case R.id.iv_voice:
200 | hideSoftInputFromWindow(ivVoice);
201 | extraView.setVisibility(GONE);
202 | // vpExtra.setVisibility(GONE);
203 | ivVoice.setSelected(!ivVoice.isSelected());
204 | if (ivVoice.isSelected()) {
205 | showVoice();
206 | } else {
207 | showInput();
208 | showSoftInputFromWindow(etInput);
209 | }
210 | break;
211 | case R.id.iv_emoji:
212 | ivEmoji.setSelected(!ivEmoji.isSelected());
213 | if (ivEmoji.isSelected()) {
214 | extraView.setVisibility(VISIBLE);
215 | // vpExtra.setVisibility(VISIBLE);
216 | vpExtra.setAdapter(pagerAdapter);
217 | pointIndicator.setIndicatorCount(vpExtra.getAdapter().getCount());
218 | //TODO vpExtra 绑定表情PagerAdapter 这里统一绑定了ActionsPagerAdapter
219 | setInputMode();
220 | hideSoftInputFromWindow(etInput);
221 | } else {
222 | setInputMode();
223 | showSoftInputFromWindow(etInput);
224 | }
225 | if (null != mChattingFooterListener) {
226 | mChattingFooterListener.OnInEditMode();
227 | }
228 | break;
229 | case R.id.iv_extra:
230 | extraView.setVisibility(VISIBLE);
231 | // vpExtra.setVisibility(VISIBLE);
232 | vpExtra.setAdapter(pagerAdapter);
233 | pointIndicator.setIndicatorCount(vpExtra.getAdapter().getCount());
234 | setInputMode();
235 | hideSoftInputFromWindow(ivExtra);
236 | btnVoice.setVisibility(GONE);
237 | etInput.setVisibility(VISIBLE);
238 | if (null != mChattingFooterListener) {
239 | mChattingFooterListener.OnInEditMode();
240 | }
241 | break;
242 | case R.id.btn_send:
243 | if (null != mChattingFooterListener) {
244 | mChattingFooterListener.OnSendTextMessageRequest(etInput.getText());
245 | etInput.setText("");
246 | }
247 | break;
248 | case R.id.btn_voice:
249 | Log.d("tag", "btn_voice");
250 | break;
251 | default:
252 | break;
253 | }
254 | }
255 |
256 | private void setInputMode() {
257 | if (null != mActivity) {
258 | if (extraView.getVisibility() == VISIBLE) {
259 | mActivity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
260 |
261 | } else {
262 | mActivity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
263 | }
264 | }
265 | }
266 |
267 | private void showInput() {
268 | ivVoice.setImageResource(R.mipmap.chatting_voice_n);
269 | btnVoice.setVisibility(GONE);
270 | etInput.setVisibility(VISIBLE);
271 | vLine.setVisibility(VISIBLE);
272 | ivEmoji.setVisibility(VISIBLE);
273 | ivEmoji.setSelected(false);
274 | showSendOrExtra();
275 | }
276 |
277 | private void showSendOrExtra() {
278 | if (TextUtils.isEmpty(etInput.getText())) {
279 | ivExtra.setVisibility(VISIBLE);
280 | btnSend.setVisibility(GONE);
281 | } else {
282 | ivExtra.setVisibility(GONE);
283 | btnSend.setVisibility(VISIBLE);
284 | }
285 | }
286 |
287 | private void showVoice() {
288 | ivVoice.setImageResource(R.mipmap.chatting_keyboard_n);
289 | btnVoice.setVisibility(VISIBLE);
290 | etInput.setVisibility(GONE);
291 | vLine.setVisibility(GONE);
292 | ivEmoji.setVisibility(GONE);
293 | ivExtra.setVisibility(VISIBLE);
294 | btnSend.setVisibility(GONE);
295 |
296 | }
297 |
298 |
299 | public void hideSoftInputFromWindow(View view) {
300 | InputMethodManager inputMethodManager = (InputMethodManager) view
301 | .getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
302 | if (inputMethodManager == null) {
303 | return;
304 | }
305 | inputMethodManager.hideSoftInputFromWindow(view.getApplicationWindowToken(), 0);
306 | }
307 |
308 | public void showSoftInputFromWindow(View view) {
309 | Log.d("tag", "keyboardHeight" + keyboardHeight);
310 | if (extraView.getVisibility() == VISIBLE && keyboardHeight > 200) {
311 | mActivity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
312 | } else {
313 | extraView.setVisibility(GONE);
314 | mActivity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
315 | }
316 | InputMethodManager inputMethodManager = (InputMethodManager) view
317 | .getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
318 | if (inputMethodManager == null) {
319 | return;
320 | }
321 | inputMethodManager.showSoftInput(view, InputMethodManager.SHOW_FORCED);
322 |
323 | }
324 |
325 | @Override
326 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
327 |
328 | }
329 |
330 | @Override
331 | public void onTextChanged(CharSequence s, int start, int before, int count) {
332 |
333 | }
334 |
335 | @Override
336 | public void afterTextChanged(Editable s) {
337 | showSendOrExtra();
338 | }
339 |
340 | public static boolean checkDeviceHasNavigationBar(Context activity) {
341 | int id = activity.getResources().getIdentifier("config_showNavigationBar", "bool", "android");
342 | return (id > 0 && activity.getResources().getBoolean(id));
343 | }
344 |
345 | @Override
346 | public void onGlobalLayout() {
347 | Rect rootRect = new Rect();
348 | parentRoot.getWindowVisibleDisplayFrame(rootRect);
349 | int screenHeight = parentRoot.getRootView()
350 | .getHeight();
351 | int heightDifference = screenHeight
352 | - (rootRect.bottom - rootRect.top);
353 |
354 | if (heightDifference < 350 && heightDifference > 20) {
355 | extraBarHeight = heightDifference;
356 | }
357 | if (heightDifference > 350 && extraBarHeight > 20) {
358 | keyboardHeight = heightDifference - extraBarHeight;
359 | extraLp.height = keyboardHeight;
360 | PrefUtils.setKeyboardHeight(mContext, keyboardHeight);
361 |
362 | }
363 |
364 | // int statusResourceId = this.getResources()
365 | // .getIdentifier("status_bar_height",
366 | // "dimen", "android");
367 | // int navigationResourceId = this.getResources().getIdentifier("navigation_bar_height", "dimen", "android");
368 | // Log.d("tag", "onGlobalLayout " + "statusResourceId " + statusResourceId + " " + heightDifference + " " + rootRect.height() + " " + screenHeight);
369 | // Log.d("tag", "onGlobalLayout " + "navigationResourceId " + navigationResourceId);
370 | // int paddingTop;
371 | // if (statusResourceId > 0) {
372 | // paddingTop = parentRoot.getResources()
373 | // .getDimensionPixelSize(statusResourceId);
374 | // heightDifference -= paddingTop;
375 | // Log.d("tag", "onGlobalLayout paddingTop" + paddingTop);
376 | // }
377 | // if (navigationResourceId > 0&&deviceHasNavigationBar) {
378 | // paddingTop = parentRoot.getResources()
379 | // .getDimensionPixelSize(navigationResourceId);
380 | // heightDifference -= paddingTop;
381 | // Log.d("tag", "onGlobalLayout paddingTop" + paddingTop);
382 | // }
383 | // if (heightDifference > 200) {
384 | // keyboardHeight = heightDifference;
385 | // PrefUtils.setKeyboardHeight(mContext, keyboardHeight);
386 | // extraLp.height = keyboardHeight;
387 | //
388 | // }
389 |
390 | }
391 |
392 | public boolean isShowExtra() {
393 | return extraView.getVisibility() == VISIBLE;
394 | }
395 |
396 | public void hideExtra() {
397 | extraView.setVisibility(GONE);
398 | }
399 |
400 | public long getAvailableSize() {
401 |
402 | File path = Environment.getExternalStorageDirectory(); //取得sdcard文件路径
403 | StatFs stat = new StatFs(path.getPath());
404 | long blockSize = stat.getBlockSize();
405 | long availableBlocks = stat.getAvailableBlocks();
406 | return (availableBlocks * blockSize) / 1024 / 1024;// MIB单位
407 | }
408 |
409 | long currentTimeMillis = 0;
410 |
411 | @Override
412 | public boolean onTouch(View v, MotionEvent event) {
413 | if (getAvailableSize() < 10) {
414 | Log.d("tag", "sdcard no memory ");
415 | Toast.makeText(mContext, "储存卡存储空间不足", Toast.LENGTH_SHORT).show();
416 | return false;
417 | }
418 |
419 | long time = System.currentTimeMillis() - currentTimeMillis;
420 | if (time <= 300) {
421 | Log.d("tag", "nvalid click");
422 | currentTimeMillis = System.currentTimeMillis();
423 | return false;
424 | }
425 |
426 | if (!isExistExternalStore()) {
427 | Toast.makeText(mContext, "储存卡已拔出,语音功能将暂时不可用", Toast.LENGTH_SHORT).show();
428 | return false;
429 |
430 | }
431 |
432 | switch (event.getAction()) {
433 | case MotionEvent.ACTION_DOWN:
434 | Log.d("tag", "ChatFooter voice recording action down");
435 | if (mChattingFooterListener != null) {
436 | mChattingFooterListener.OnVoiceRcdInitReuqest();
437 | }
438 | btnVoice.setText("松开 结束");
439 | break;
440 |
441 | case MotionEvent.ACTION_MOVE:
442 | if (popupWindow == null) {
443 | return false;
444 | }
445 | if (event.getX() <= 0.0F || event.getY() <= -CANCEL_DISTANCE || event.getX() >= btnVoice.getWidth()) {
446 | Log.d("tag", "show cancel Tips");
447 | mVoiceRcdHitCancelView.setVisibility(View.VISIBLE);
448 | mVoiceHintAnimArea.setVisibility(View.GONE);
449 | btnVoice.setText(R.string.chatfooter_cancel_rcd_release);
450 |
451 | } else {
452 | btnVoice.setText("松开 结束");
453 | popupWindow.showAtLocation(parentRoot, Gravity.CENTER, 0, 0);
454 | popupWindow.setOutsideTouchable(true);
455 | popupWindow.update();
456 | Log.d("tag", "show rcd animation Tips");
457 | mVoiceRcdHitCancelView.setVisibility(View.GONE);
458 | mVoiceHintAnimArea.setVisibility(View.VISIBLE);
459 | }
460 |
461 | break;
462 | case MotionEvent.ACTION_UP:
463 | popupWindow.dismiss();
464 | Log.d("tag", "ChatFooter voice recording action up ");
465 | btnVoice.setText("按住 请说话");
466 | break;
467 | }
468 |
469 | return false;
470 | }
471 |
472 | /**
473 | * 是否有外存卡
474 | *
475 | * @return
476 | */
477 | public static boolean isExistExternalStore() {
478 | if (Environment.getExternalStorageState().equals(
479 | Environment.MEDIA_MOUNTED)) {
480 | return true;
481 | } else {
482 | return false;
483 | }
484 | }
485 |
486 | public interface OnChattingFooterListener {
487 |
488 | void OnVoiceRcdInitReuqest();
489 |
490 | void OnVoiceRcdStartRequest();
491 |
492 | /**
493 | * Called when the voce record button nomal and cancel send voice.
494 | */
495 | void OnVoiceRcdCancelRequest();
496 |
497 | /**
498 | * Called when the voce record button nomal and send voice.
499 | */
500 | void OnVoiceRcdStopRequest();
501 |
502 | void OnSendTextMessageRequest(CharSequence text);
503 |
504 | void OnUpdateTextOutBoxRequest(CharSequence text);
505 |
506 | void OnSendCustomEmojiRequest(int emojiid, String emojiName);
507 |
508 | void OnEmojiDelRequest();
509 |
510 | void OnInEditMode();
511 |
512 | void onPause();
513 |
514 | void onResume();
515 |
516 | void release();
517 | }
518 |
519 |
520 | }
521 |
--------------------------------------------------------------------------------