├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable
│ │ │ │ ├── bg.9.png
│ │ │ │ ├── input_bg.9.png
│ │ │ │ ├── send_btn_bg.xml
│ │ │ │ ├── toolbar_divider.xml
│ │ │ │ ├── send_msg_bg.xml
│ │ │ │ ├── time_stamp_bg.xml
│ │ │ │ ├── badge.xml
│ │ │ │ ├── et_underline_style.xml
│ │ │ │ ├── receive_msg_bg.xml
│ │ │ │ ├── et_underline_unselected.xml
│ │ │ │ ├── et_underline_selected.xml
│ │ │ │ └── ic_launcher_background.xml
│ │ │ ├── drawable-hdpi
│ │ │ │ ├── add.png
│ │ │ │ └── search.png
│ │ │ ├── drawable-mdpi
│ │ │ │ ├── add.png
│ │ │ │ ├── search.png
│ │ │ │ └── profile_info.png
│ │ │ ├── drawable-xhdpi
│ │ │ │ ├── add.png
│ │ │ │ ├── mute.png
│ │ │ │ ├── icon_add.png
│ │ │ │ ├── icon_emoji.png
│ │ │ │ ├── icon_voice.png
│ │ │ │ └── profile_info.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ ├── add.png
│ │ │ │ ├── back.png
│ │ │ │ ├── group1.jpg
│ │ │ │ ├── hdimg_1.jpg
│ │ │ │ ├── hdimg_2.jpg
│ │ │ │ ├── hdimg_3.jpg
│ │ │ │ ├── hdimg_4.png
│ │ │ │ ├── hdimg_5.png
│ │ │ │ ├── hdimg_6.jpg
│ │ │ │ ├── search.png
│ │ │ │ ├── stranger.png
│ │ │ │ ├── unread.png
│ │ │ │ ├── user_2.png
│ │ │ │ ├── user_3.png
│ │ │ │ ├── user_4.png
│ │ │ │ ├── user_5.png
│ │ │ │ └── profile_info.png
│ │ │ ├── drawable-xxxhdpi
│ │ │ │ ├── add.png
│ │ │ │ ├── back.png
│ │ │ │ ├── search.png
│ │ │ │ ├── profile_info.png
│ │ │ │ ├── bubble_sender_normal.9.png
│ │ │ │ ├── bubble_receiver_normal.9.png
│ │ │ │ ├── bubble_sender_pressed.9.png
│ │ │ │ └── bubble_receiver_pressed.9.png
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_laucnher.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── anim
│ │ │ │ ├── popupwindow_show_anim_from_left_top.xml
│ │ │ │ ├── popupwindow_show_anim_from_left_bottom.xml
│ │ │ │ ├── popupwindow_show_anim_from_right_top.xml
│ │ │ │ ├── popupwindow_show_anim_from_right_bottom.xml
│ │ │ │ ├── popupwindow_hide_anim_from_left_top.xml
│ │ │ │ ├── popupwindow_hide_anim_from_left_bottom.xml
│ │ │ │ ├── popupwindow_hide_anim_from_right_bottom.xml
│ │ │ │ └── popupwindow_hide_anim_from_right_top.xml
│ │ │ ├── layout
│ │ │ │ ├── popup_menu_general_layout.xml
│ │ │ │ ├── item_menu.xml
│ │ │ │ ├── activity_wechat_main.xml
│ │ │ │ ├── item_wechat_msg_list.xml
│ │ │ │ ├── item_wechat_main.xml
│ │ │ │ └── activity_wechat_chat.xml
│ │ │ ├── values
│ │ │ │ ├── dimen.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── strings.xml
│ │ │ │ └── styles.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── shawn
│ │ │ └── fakewechat
│ │ │ ├── bean
│ │ │ ├── MsgData.java
│ │ │ └── ContactShowInfo.java
│ │ │ ├── adapter
│ │ │ ├── SimpleMenuAdapter.java
│ │ │ ├── ContactAdapter.java
│ │ │ └── ChatAdapter.java
│ │ │ ├── component
│ │ │ ├── ListViewSelfAdapt.java
│ │ │ └── PopupMenuWindows.java
│ │ │ ├── utils
│ │ │ └── HelpUtils.java
│ │ │ ├── ChatActivity.java
│ │ │ └── WechatActivity.java
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── shawn
│ │ │ └── fakewechat
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── shawn
│ │ └── fakewechat
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── image
├── 1.png
└── 2.png
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .idea
├── dictionaries
│ └── fengshawn.xml
├── modules.xml
├── runConfigurations.xml
├── gradle.xml
└── misc.xml
├── .gitignore
├── README.md
├── gradle.properties
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/image/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/image/1.png
--------------------------------------------------------------------------------
/image/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/image/2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable/bg.9.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/.idea/dictionaries/fengshawn.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-hdpi/add.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-mdpi/add.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xhdpi/add.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/mute.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xhdpi/mute.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/add.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/input_bg.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable/input_bg.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-hdpi/search.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-mdpi/search.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/back.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxxhdpi/add.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxxhdpi/back.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/icon_add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xhdpi/icon_add.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/group1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/group1.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/hdimg_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/hdimg_1.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/hdimg_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/hdimg_2.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/hdimg_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/hdimg_3.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/hdimg_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/hdimg_4.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/hdimg_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/hdimg_5.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/hdimg_6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/hdimg_6.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/search.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/stranger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/stranger.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/unread.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/unread.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/user_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/user_2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/user_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/user_3.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/user_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/user_4.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/user_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/user_5.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxxhdpi/search.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_laucnher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/mipmap-mdpi/ic_laucnher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/profile_info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-mdpi/profile_info.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/icon_emoji.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xhdpi/icon_emoji.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/icon_voice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xhdpi/icon_voice.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/profile_info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xhdpi/profile_info.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/profile_info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxhdpi/profile_info.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/profile_info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxxhdpi/profile_info.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/bubble_sender_normal.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxxhdpi/bubble_sender_normal.9.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/bubble_receiver_normal.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxxhdpi/bubble_receiver_normal.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/bubble_sender_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxxhdpi/bubble_sender_pressed.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/bubble_receiver_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fx4758/FakeWechat/HEAD/app/src/main/res/drawable-xxxhdpi/bubble_receiver_pressed.9.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat May 05 15:06:23 CST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/send_btn_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/toolbar_divider.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/send_msg_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/time_stamp_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/badge.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/et_underline_style.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/receive_msg_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/popupwindow_show_anim_from_left_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/popupwindow_show_anim_from_left_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/popupwindow_show_anim_from_right_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/popupwindow_show_anim_from_right_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/popupwindow_hide_anim_from_left_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/popupwindow_hide_anim_from_left_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/popupwindow_hide_anim_from_right_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/popupwindow_hide_anim_from_right_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/shawn/fakewechat/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.shawn.fakewechat;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FakeWechat
2 | 高仿微信界面,主要包含联系人界面,聊天界面,主要组件:跟随手指自适应弹出菜单
3 |
4 | 解析请见:
5 |
6 | [高仿微信跟随手指自适应弹出菜单](https://blog.csdn.net/fx475870569/article/details/80232691)
7 |
8 | [高仿微信界面](https://blog.csdn.net/fx475870569/article/details/80231774)
9 |
10 | ## 包含以下主要功能
11 | #### 跟随手指位置自适应弹出菜单
12 | #### 标记未读
13 | #### 置顶聊天
14 | #### 自动回复消息
15 | #### 自动添加Time Stamp
16 |
17 | 
18 | 
19 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/et_underline_unselected.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/et_underline_selected.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/popup_menu_general_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
16 |
--------------------------------------------------------------------------------
/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 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/shawn/fakewechat/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.shawn.fakewechat;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.shawn.fakewechat", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimen.xml:
--------------------------------------------------------------------------------
1 |
2 | 68dp
3 | 52dp
4 | 5dip
5 | 10dip
6 | 15dp
7 | 10dip
8 | 18sp
9 | 16sp
10 | 14sp
11 | 12sp
12 | 8sp
13 | 8sp
14 | 8dp
15 | 52dp
16 | 60dp
17 | 60dp
18 | 55dp
19 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #ececec
6 | #0fececec
7 | #00ffffff
8 | #abaaaa
9 | #FF4081
10 | #ff4043
11 | #ffffff
12 | #2cf903
13 | #02c7fd
14 | #000000
15 | #464644
16 | #40403e
17 | #3e3e3d
18 | #02bf1f
19 | #10000000
20 | #678cb9
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Fake Wechat
3 | Fiona
4 | username
5 | 姓名
6 | 电话号码
7 | 提 交
8 | 邮箱
9 | 家庭住址
10 | 公司
11 |
12 | [Audio]
13 | 标为未读
14 | 置顶聊天
15 | 删除聊天
16 | 取消关注
17 | 12:12
18 | 1
19 | 微信
20 | 未知好友
21 | 发送
22 | 空白消息
23 | 登陆
24 | 删除
25 | 更多
26 |
27 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 26
5 | defaultConfig {
6 | applicationId "com.shawn.fakewechat"
7 | minSdkVersion 19
8 | targetSdkVersion 26
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | compileOptions {
20 | targetCompatibility 1.8
21 | sourceCompatibility 1.8
22 | }
23 | }
24 |
25 | dependencies {
26 | implementation fileTree(dir: 'libs', include: ['*.jar'])
27 | implementation 'com.android.support:appcompat-v7:26.1.0'
28 | implementation 'com.android.support.constraint:constraint-layout:1.1.0'
29 | testImplementation 'junit:junit:4.12'
30 | compile 'com.android.support:recyclerview-v7:26.1.0'
31 | compile 'com.readystatesoftware.systembartint:systembartint:1.0.4'
32 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
33 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/shawn/fakewechat/bean/MsgData.java:
--------------------------------------------------------------------------------
1 | package com.shawn.fakewechat.bean;
2 |
3 | /**
4 | * Created by fengshawn on 2017/8/10.
5 | */
6 |
7 | public class MsgData {
8 | private String msg;
9 | private long timeStamp;
10 | private int profile_res;
11 | private int msgType;
12 |
13 | public MsgData(String msg, long timeStamp, int hd_img_res, int msgType) {
14 | this.msg = msg;
15 | this.timeStamp = timeStamp;
16 | this.profile_res = hd_img_res;
17 | this.msgType = msgType;
18 | }
19 |
20 | public int getMsgType() {
21 | return msgType;
22 | }
23 |
24 | public void setMsgType(int msgType) {
25 | this.msgType = msgType;
26 | }
27 |
28 | public String getMsg() {
29 | return msg;
30 | }
31 |
32 | public void setMsg(String msg) {
33 | this.msg = msg;
34 | }
35 |
36 | public long getTimeStamp() {
37 | return timeStamp;
38 | }
39 |
40 | public void setTimeStamp(long timeStamp) {
41 | this.timeStamp = timeStamp;
42 | }
43 |
44 | public int getProfile_res() {
45 | return profile_res;
46 | }
47 |
48 | public void setProfile_res(int profile_res) {
49 | this.profile_res = profile_res;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
15 |
16 |
20 |
24 |
28 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/shawn/fakewechat/adapter/SimpleMenuAdapter.java:
--------------------------------------------------------------------------------
1 | package com.shawn.fakewechat.adapter;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.LayoutRes;
5 | import android.support.annotation.NonNull;
6 | import android.support.annotation.Nullable;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.ArrayAdapter;
11 | import android.widget.TextView;
12 |
13 |
14 | import com.shawn.fakewechat.R;
15 |
16 | import java.util.List;
17 |
18 | /**
19 | * Created by fengshawn on 2017/8/3.
20 | */
21 |
22 | public class SimpleMenuAdapter extends ArrayAdapter {
23 |
24 | private Context context;
25 | private List list;
26 | private int resource;
27 |
28 | public SimpleMenuAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List list) {
29 | super(context, resource, list);
30 | this.context = context;
31 | this.list = list;
32 | this.resource = resource;
33 | }
34 |
35 | @NonNull
36 | @Override
37 | public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
38 |
39 | View v = LayoutInflater.from(context).inflate(resource, parent, false);
40 | ViewHolder vh = new ViewHolder(v);
41 | vh.tv.setText((String) list.get(position));
42 | //v.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
43 | //Log.i("tag", v + " height: " + v.getMeasuredHeight());
44 | return v;
45 | }
46 |
47 | class ViewHolder {
48 | TextView tv;
49 |
50 | public ViewHolder(View v) {
51 | tv = (TextView) v.findViewById(R.id.item_menu_tv);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/shawn/fakewechat/bean/ContactShowInfo.java:
--------------------------------------------------------------------------------
1 | package com.shawn.fakewechat.bean;
2 |
3 | /**
4 | * Created by fengshawn on 2017/8/2.
5 | */
6 |
7 | public class ContactShowInfo {
8 |
9 | private int headImage;
10 | private String username;
11 | private String lastMsg;
12 | private String lastMsgTime;
13 | private boolean isMute;
14 | private boolean isRead;
15 | private int accountType;
16 |
17 | public ContactShowInfo(int headImage, String username, String lastMsg, String lastMsgTime, boolean isMute, boolean isRead, int accountType) {
18 | this.headImage = headImage;
19 | this.username = username;
20 | this.lastMsg = lastMsg;
21 | this.lastMsgTime = lastMsgTime;
22 | this.isMute = isMute;
23 | this.isRead = isRead;
24 | this.accountType = accountType;
25 | }
26 |
27 | public boolean isRead() {
28 | return isRead;
29 | }
30 |
31 | public void setRead(boolean read) {
32 | isRead = read;
33 | }
34 |
35 |
36 | public int getHeadImage() {
37 | return headImage;
38 | }
39 |
40 | public void setHeadImage(int headImage) {
41 | this.headImage = headImage;
42 | }
43 |
44 | public String getUsername() {
45 | return username;
46 | }
47 |
48 | public void setUsername(String username) {
49 | this.username = username;
50 | }
51 |
52 | public String getLastMsg() {
53 | return lastMsg;
54 | }
55 |
56 | public void setLastMsg(String lastMsg) {
57 | this.lastMsg = lastMsg;
58 | }
59 |
60 | public String getLastMsgTime() {
61 | return lastMsgTime;
62 | }
63 |
64 | public void setLastMsgTime(String lastMsgTime) {
65 | this.lastMsgTime = lastMsgTime;
66 | }
67 |
68 | public boolean isMute() {
69 | return isMute;
70 | }
71 |
72 | public void setMute(boolean mute) {
73 | isMute = mute;
74 | }
75 |
76 | public int getAccountType() {
77 | return accountType;
78 | }
79 |
80 | public void setAccountType(int accountType) {
81 | this.accountType = accountType;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_wechat_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
20 |
26 |
27 |
35 |
36 |
44 |
45 |
46 |
47 |
48 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/.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 | 1.8
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/app/src/main/java/com/shawn/fakewechat/adapter/ContactAdapter.java:
--------------------------------------------------------------------------------
1 | package com.shawn.fakewechat.adapter;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.LayoutRes;
5 | import android.support.annotation.NonNull;
6 | import android.support.annotation.Nullable;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.ArrayAdapter;
11 | import android.widget.ImageView;
12 | import android.widget.TextView;
13 |
14 | import com.shawn.fakewechat.R;
15 | import com.shawn.fakewechat.bean.ContactShowInfo;
16 |
17 | import java.util.List;
18 |
19 | /**
20 | * Created by fengshawn on 2017/8/2.
21 | */
22 |
23 | public class ContactAdapter extends ArrayAdapter {
24 |
25 | private List contactInfos;
26 | private Context context;
27 | private int resource;
28 |
29 |
30 | public ContactAdapter(@NonNull Context context, @LayoutRes int resource, List contactInfos) {
31 | super(context, resource, contactInfos);
32 | this.contactInfos = contactInfos;
33 | this.context = context;
34 | this.resource = resource;
35 | }
36 |
37 | @NonNull
38 | @Override
39 | public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
40 | View view;
41 | ViewHolder vh;
42 | ContactShowInfo contactInfo = contactInfos.get(position);
43 |
44 | if (convertView == null) {
45 | view = LayoutInflater.from(context).inflate(resource, parent, false);
46 | vh = new ViewHolder(view);
47 | view.setTag(vh);
48 | } else {
49 | view = convertView;
50 | vh = (ViewHolder) view.getTag();
51 | }
52 | //初始化item控件
53 | vh.headImg.setImageResource(contactInfo.getHeadImage());
54 | vh.lastMsgTime.setText(contactInfo.getLastMsgTime());
55 | vh.username.setText(contactInfo.getUsername());
56 | vh.lastMsg.setText(contactInfo.getLastMsg());
57 | if (contactInfo.isMute())
58 | vh.mute.setVisibility(View.VISIBLE);
59 | else
60 | vh.mute.setVisibility(View.GONE);
61 |
62 | if (contactInfo.isRead()) {
63 | vh.badge.setVisibility(View.GONE);
64 | } else {
65 | vh.badge.setVisibility(View.VISIBLE);
66 | }
67 |
68 | return view;
69 | }
70 |
71 | class ViewHolder {
72 | private TextView username, lastMsg, lastMsgTime, badge;
73 | private ImageView headImg, mute;
74 |
75 | public ViewHolder(View v) {
76 | username = (TextView) v.findViewById(R.id.item_wechat_main_tv_username);
77 | lastMsg = (TextView) v.findViewById(R.id.item_wechat_main_tv_lastmsg);
78 | lastMsgTime = (TextView) v.findViewById(R.id.item_wechat_main_tv_time);
79 | headImg = (ImageView) v.findViewById(R.id.item_wechat_main_iv_headimg);
80 | mute = (ImageView) v.findViewById(R.id.item_wechat_main_iv_mute);
81 | badge = (TextView) v.findViewById(R.id.item_wechat_main_iv_unread);
82 | }
83 | }
84 |
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/main/java/com/shawn/fakewechat/component/ListViewSelfAdapt.java:
--------------------------------------------------------------------------------
1 | package com.shawn.fakewechat.component;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.ListAdapter;
8 | import android.widget.ListView;
9 |
10 | /**
11 | * Created by fengshawn on 2017/8/7.
12 | */
13 |
14 | public class ListViewSelfAdapt extends ListView {
15 |
16 |
17 | public ListViewSelfAdapt(Context context) {
18 | super(context);
19 | }
20 |
21 | public ListViewSelfAdapt(Context context, AttributeSet attrs) {
22 | super(context, attrs);
23 | }
24 |
25 | public ListViewSelfAdapt(Context context, AttributeSet attrs, int defStyleAttr) {
26 | super(context, attrs, defStyleAttr);
27 | }
28 |
29 | @Override
30 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
31 | int width = getMaxChildWidth() + getPaddingLeft() + getPaddingRight();
32 | int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
33 |
34 | super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), expandSpec);
35 | }
36 |
37 | private final int getMaxChildWidth() {
38 | int max = 0;
39 | ListAdapter adapter = getAdapter();
40 | int childCount = adapter == null ? 0 : adapter.getCount();
41 | View childView = null;
42 | if (childCount > 0) {
43 | for (int i = 0; i < childCount; i++) {
44 | childView = adapter.getView(i, childView, this);
45 | childView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
46 | max = max > childView.getMeasuredWidth() ? max : childView.getMeasuredWidth();
47 | }
48 | }
49 | ViewGroup.LayoutParams params = getLayoutParams();
50 | params.width = max;
51 | setLayoutParams(params);
52 |
53 | return max;
54 | }
55 |
56 | /**
57 | * 计算自适应高度,但是子布局必须LinearLayout,且在数据更新完成后,
58 | * 例如:
59 | * mAdapter.notifyDataSetChanged();
60 | * Function.getTotalHeightofListView(mListView);
61 | * @return
62 | */
63 | private int getSelfAdapterHeight() {
64 | int totalHeight = 0;
65 |
66 | //计算所有item高度
67 | ListAdapter adapter = getAdapter();
68 | int childCount = adapter == null ? 0 : adapter.getCount();
69 | View childView = null;
70 | if (childCount > 0) {
71 | for (int i = 0; i < childCount; i++) {
72 | childView = adapter.getView(i, null, this);
73 | childView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
74 | totalHeight += childView.getMeasuredHeight();
75 | }
76 | //计算所有分割线高度
77 | totalHeight += getDividerHeight() * (childCount - 1);
78 | }
79 | ViewGroup.LayoutParams params = getLayoutParams();
80 |
81 | params.height = totalHeight;
82 |
83 | // listView.getDividerHeight()获取子项间分隔符占用的高度
84 |
85 | // params.height最后得到整个ListView完整显示需要的高度
86 |
87 | setLayoutParams(params);
88 |
89 | return totalHeight;
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_wechat_msg_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
19 |
20 |
27 |
28 |
33 |
34 |
48 |
49 |
50 |
56 |
57 |
72 |
73 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_wechat_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
14 |
21 |
22 |
33 |
34 |
35 |
42 |
43 |
52 |
53 |
62 |
63 |
64 |
65 |
72 |
73 |
81 |
82 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/app/src/main/java/com/shawn/fakewechat/utils/HelpUtils.java:
--------------------------------------------------------------------------------
1 | package com.shawn.fakewechat.utils;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.Activity;
5 | import android.content.Context;
6 | import android.content.res.Resources;
7 | import android.view.Window;
8 | import android.view.WindowManager;
9 |
10 | import com.readystatesoftware.systembartint.SystemBarTintManager;
11 | import com.shawn.fakewechat.R;
12 |
13 | import java.text.SimpleDateFormat;
14 | import java.util.Calendar;
15 | import java.util.Date;
16 |
17 | /**
18 | * Created by fengshawn on 2017/8/8.
19 | */
20 |
21 | public class HelpUtils {
22 |
23 | public static int getScreenHeight(Context context) {
24 | return context.getResources().getDisplayMetrics().heightPixels;
25 | }
26 |
27 | public static int getScreenWidth(Context context) {
28 | return context.getResources().getDisplayMetrics().widthPixels;
29 | }
30 |
31 | public static int getStatusBarHeight(Context context) {
32 | Resources res = context.getResources();
33 | int resId = res.getIdentifier("status_bar_height", "dimen", "android");
34 | return res.getDimensionPixelSize(resId);
35 |
36 | //也可以通过反射
37 | /**
38 | Class> c = null;
39 | Object obj = null;
40 | Field field = null;
41 | int x = 0, statusBarHeight = 0;
42 | try {
43 | c = Class.forName("com.android.internal.R$dimen");
44 | obj = c.newInstance();
45 | field = c.getField("status_bar_height");
46 | x = Integer.parseInt(field.get(obj).toString());
47 | statusBarHeight = context.getResources().getDimensionPixelSize(x);
48 | } catch (Exception e1) {
49 | e1.printStackTrace();
50 | }
51 | return statusBarHeight;
52 | */
53 | }
54 |
55 | /**
56 | * 用户友好时间显示
57 | *
58 | * @param nowTime 现在时间毫秒
59 | * @param preTime 之前时间毫秒
60 | * @return 符合用户习惯的时间显示
61 | */
62 | public static String calculateShowTime(long nowTime, long preTime) {
63 | if (nowTime <= 0 || preTime <= 0)
64 | return null;
65 | SimpleDateFormat format = new SimpleDateFormat("yy-MM-dd-HH-mm-E");
66 | String now = format.format(new Date(nowTime));
67 | String pre = format.format(new Date(preTime));
68 | String[] nowTimeArr = now.split("-");
69 | String[] preTimeArr = pre.split("-");
70 | //当天以内,年月日相同,超过一分钟显示
71 | if (nowTimeArr[0].equals(preTimeArr[0]) && nowTimeArr[1].equals(preTimeArr[1]) && nowTimeArr[2].equals(preTimeArr[2]) && nowTime - preTime > 60000) {
72 | return preTimeArr[3] + ":" + preTimeArr[4];
73 | }
74 | //一周以内
75 | else if (Integer.valueOf(nowTimeArr[2]) - Integer.valueOf(preTimeArr[2]) > 0 && nowTime - preTime < 7 * 24 * 60 * 60 * 1000) {
76 |
77 | if (Integer.valueOf(nowTimeArr[2]) - Integer.valueOf(preTimeArr[2]) == 1)
78 | return "昨天 " + preTimeArr[3] + ":" + preTimeArr[4];
79 | else
80 | return preTimeArr[5] + " " + preTimeArr[3] + ":" + preTimeArr[4];
81 | }
82 | //一周以上
83 | else if (nowTime - preTime > 7 * 24 * 60 * 60 * 1000) {
84 | return preTimeArr[0] + "年" + preTimeArr[1] + "月" + preTimeArr[2] + "日" + " " + preTimeArr[3] + ":" + preTimeArr[4];
85 | }
86 | return null;
87 | }
88 |
89 |
90 | public static long getCurrentMillisTime() {
91 | return System.currentTimeMillis();
92 | }
93 |
94 |
95 | public static void transparentNav(Activity activity) {
96 | setTransparentStatus(true, activity);
97 | SystemBarTintManager tintManager = new SystemBarTintManager(activity);
98 | tintManager.setStatusBarTintEnabled(true);
99 | tintManager.setStatusBarTintResource(R.color.colorDark);
100 | }
101 |
102 | @TargetApi(19)
103 | private static void setTransparentStatus(boolean on, Activity activity) {
104 | Window win = activity.getWindow();
105 | WindowManager.LayoutParams params = win.getAttributes();
106 | final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
107 | if (on)
108 | params.flags |= bits;
109 | else
110 | params.flags &= ~bits;
111 |
112 | win.setAttributes(params);
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/app/src/main/java/com/shawn/fakewechat/adapter/ChatAdapter.java:
--------------------------------------------------------------------------------
1 | package com.shawn.fakewechat.adapter;
2 |
3 | import android.content.Context;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.ImageView;
9 | import android.widget.LinearLayout;
10 | import android.widget.RelativeLayout;
11 | import android.widget.TextView;
12 |
13 | import com.shawn.fakewechat.R;
14 | import com.shawn.fakewechat.ChatActivity;
15 | import com.shawn.fakewechat.bean.MsgData;
16 | import com.shawn.fakewechat.utils.HelpUtils;
17 |
18 | import java.util.List;
19 |
20 | /**
21 | * Created by fengshawn on 2017/8/10.
22 | */
23 |
24 | public class ChatAdapter extends RecyclerView.Adapter {
25 |
26 | private List listData;
27 | private Context context;
28 |
29 | public ChatAdapter(Context context, List listData) {
30 | this.context = context;
31 | this.listData = listData;
32 | }
33 |
34 | @Override
35 | public MsgViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
36 | View view = LayoutInflater.from(context).inflate(R.layout.item_wechat_msg_list, parent, false);
37 | return new MsgViewHolder(view);
38 | }
39 |
40 | @Override
41 | public void onBindViewHolder(MsgViewHolder holder, int position) {
42 | MsgData currentMsgData = listData.get(position);
43 | MsgData preMsgData = null;
44 | if (position >= 1)
45 | preMsgData = listData.get(position - 1);
46 | switch (currentMsgData.getMsgType()) {
47 | case ChatActivity.TYPE_RECEIVER_MSG:
48 | initTimeStamp(holder, currentMsgData, preMsgData);
49 | holder.senderLayout.setVisibility(View.GONE);
50 | holder.receiverLayout.setVisibility(View.VISIBLE);
51 | holder.receiveMsg.setText(currentMsgData.getMsg());
52 | holder.receiver_profile.setImageResource(currentMsgData.getProfile_res());
53 | break;
54 |
55 |
56 | case ChatActivity.TYPE_SENDER_MSG:
57 | initTimeStamp(holder, currentMsgData, preMsgData);
58 | holder.senderLayout.setVisibility(View.VISIBLE);
59 | holder.receiverLayout.setVisibility(View.GONE);
60 | holder.sendMsg.setText(currentMsgData.getMsg());
61 | holder.send_profile.setImageResource(currentMsgData.getProfile_res());
62 | break;
63 | }
64 | }
65 |
66 | private void initTimeStamp(MsgViewHolder holder, MsgData currentMsgData, MsgData preMsgData) {
67 | String showTime;
68 | if (preMsgData == null) {
69 | showTime = HelpUtils.calculateShowTime(HelpUtils.getCurrentMillisTime(), currentMsgData.getTimeStamp());
70 | } else {
71 | showTime = HelpUtils.calculateShowTime(currentMsgData.getTimeStamp(), preMsgData.getTimeStamp());
72 | }
73 | if (showTime != null) {
74 | holder.timeStamp.setVisibility(View.VISIBLE);
75 | holder.timeStamp.setText(showTime);
76 | } else {
77 | holder.timeStamp.setVisibility(View.GONE);
78 | }
79 |
80 | }
81 |
82 | @Override
83 | public int getItemCount() {
84 | return listData.size();
85 | }
86 |
87 | class MsgViewHolder extends RecyclerView.ViewHolder {
88 |
89 | ImageView receiver_profile, send_profile;
90 | TextView timeStamp, receiveMsg, sendMsg;
91 | RelativeLayout senderLayout;
92 | LinearLayout receiverLayout;
93 |
94 | public MsgViewHolder(View itemView) {
95 | super(itemView);
96 | receiver_profile = itemView.findViewById(R.id.item_wechat_msg_iv_receiver_profile);
97 | send_profile = itemView.findViewById(R.id.item_wechat_msg_iv_sender_profile);
98 | timeStamp = itemView.findViewById(R.id.item_wechat_msg_iv_time_stamp);
99 | receiveMsg = itemView.findViewById(R.id.item_wechat_msg_tv_receiver_msg);
100 | sendMsg = itemView.findViewById(R.id.item_wechat_msg_tv_sender_msg);
101 | senderLayout = itemView.findViewById(R.id.item_wechat_msg_layout_sender);
102 | receiverLayout = itemView.findViewById(R.id.item_wechat_msg_layout_receiver);
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/app/src/main/java/com/shawn/fakewechat/component/PopupMenuWindows.java:
--------------------------------------------------------------------------------
1 | package com.shawn.fakewechat.component;
2 |
3 | import android.content.Context;
4 | import android.graphics.drawable.Drawable;
5 | import android.os.Build;
6 | import android.support.v4.content.ContextCompat;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.AdapterView;
11 | import android.widget.ListAdapter;
12 | import android.widget.PopupWindow;
13 |
14 | import com.shawn.fakewechat.R;
15 | import com.shawn.fakewechat.utils.HelpUtils;
16 |
17 | /**
18 | * Created by fengshawn on 2017/8/3.
19 | */
20 |
21 | public class PopupMenuWindows extends PopupWindow {
22 |
23 | private final static int SHOW_ON_LEFT = 0X11;
24 | private final static int SHOW_ON_RIGHT = 0X12;
25 | private final static int SHOW_ON_UP = 0X13;
26 | private final static int SHOW_ON_DOWN = 0X14;
27 |
28 | private View contentView;
29 | private Context context;
30 | private int showAtVertical, showAtOrientation;
31 | private OnMenuItemClickListener listener;
32 | private ListAdapter adapter;
33 | private boolean isSetAutoFitStyle = false;
34 |
35 | public interface OnMenuItemClickListener {
36 | void onMenuItemClickListener(AdapterView> parent, View view, int position, long id);
37 | }
38 |
39 | public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
40 | this.listener = listener;
41 | }
42 |
43 | public PopupMenuWindows(Context context, int resId, ListAdapter adapter) {
44 | super(context);
45 | this.context = context;
46 | this.adapter = adapter;
47 | LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
48 | contentView = inflater.inflate(resId, null);
49 | setContentView(contentView);
50 | initPopWindow();
51 | }
52 |
53 | private void initPopWindow() {
54 | Drawable dw;
55 |
56 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
57 | setElevation(35);
58 | } else {
59 | dw = ContextCompat.getDrawable(context, R.drawable.bg);
60 | }
61 | setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
62 | setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
63 | setFocusable(true);
64 | setOutsideTouchable(true);
65 | dw = ContextCompat.getDrawable(context, R.color.colorWhite);
66 | setBackgroundDrawable(dw);
67 | ListViewSelfAdapt listViewSelfAdapt = (ListViewSelfAdapt) contentView.findViewById(R.id.popup_menu_general_layout_lv);
68 | listViewSelfAdapt.setAdapter(adapter);
69 | listViewSelfAdapt.setOnItemClickListener((parent, view, position, id) -> {
70 | listener.onMenuItemClickListener(parent, view, position, id);
71 | });
72 | }
73 |
74 |
75 | public View getContentView() {
76 | return contentView;
77 | }
78 |
79 | /**
80 | * 计算弹出位置,弹出位置空间不足的向反方向弹出
81 | *
82 | * @param posX 触摸的X坐标
83 | * @param posY 触摸的Y坐标
84 | * @return 长度为2的坐标数组, 为重新计算过的弹出位置,0为x坐标,1为y坐标
85 | */
86 | public int[] reckonPopWindowShowPos(int posX, int posY) {
87 | int screenH = HelpUtils.getScreenHeight(context);
88 | int screenW = HelpUtils.getScreenWidth(context);
89 | //popupWindow显示区域View
90 | contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
91 | int windowsHeight = contentView.getMeasuredHeight();
92 | int windowsWidth = contentView.getMeasuredWidth();
93 | int x = posX, y = posY; //窗口弹出坐标
94 |
95 | //向上弹出
96 | if (screenH - posY < windowsHeight) {
97 | y = posY - windowsHeight;
98 | showAtVertical = SHOW_ON_UP;
99 | } else { //向下弹出
100 | showAtVertical = SHOW_ON_DOWN;
101 | }
102 |
103 | //左弹出
104 | if (screenW - posX < windowsWidth) {
105 | x = posX - windowsWidth;
106 | showAtOrientation = SHOW_ON_LEFT;
107 | } else { //右弹出
108 | showAtOrientation = SHOW_ON_RIGHT;
109 | }
110 | int[] posArr = new int[2];
111 | posArr[0] = x;
112 | posArr[1] = y;
113 | //防止设置自适应动画方法在此方法之前调用
114 | if (isSetAutoFitStyle) {
115 | setAutoFitStyle(true);
116 | }
117 | return posArr;
118 | }
119 |
120 | /**
121 | * 自适应触摸位置弹出动画
122 | *
123 | * @param isSet
124 | */
125 | public void setAutoFitStyle(boolean isSet) {
126 | isSetAutoFitStyle = isSet;
127 | if (isSet) {
128 |
129 | if (showAtOrientation == SHOW_ON_RIGHT && showAtVertical == SHOW_ON_UP) {
130 | setAnimationStyle(R.style.PopupWindowAnimationLB);
131 | }
132 |
133 | if (showAtOrientation == SHOW_ON_RIGHT && showAtVertical == SHOW_ON_DOWN) {
134 | setAnimationStyle(R.style.PopupWindowAnimationLT);
135 | }
136 |
137 | if (showAtOrientation == SHOW_ON_LEFT && showAtVertical == SHOW_ON_UP) {
138 | setAnimationStyle(R.style.PopupWindowAnimationRB);
139 | }
140 |
141 | if (showAtOrientation == SHOW_ON_LEFT && showAtVertical == SHOW_ON_DOWN) {
142 | setAnimationStyle(R.style.PopupWindowAnimationRT);
143 | }
144 | }
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/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/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_wechat_chat.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
19 |
20 |
29 |
30 |
36 |
37 |
45 |
46 |
47 |
56 |
57 |
58 |
59 |
60 |
68 |
69 |
80 |
81 |
93 |
94 |
103 |
104 |
116 |
117 |
122 |
123 |
131 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
--------------------------------------------------------------------------------
/app/src/main/java/com/shawn/fakewechat/ChatActivity.java:
--------------------------------------------------------------------------------
1 | package com.shawn.fakewechat;
2 |
3 | import android.annotation.TargetApi;
4 | import android.os.Bundle;
5 | import android.os.Handler;
6 | import android.support.annotation.Nullable;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.support.v7.widget.LinearLayoutManager;
9 | import android.support.v7.widget.RecyclerView;
10 | import android.support.v7.widget.Toolbar;
11 | import android.text.Editable;
12 | import android.text.TextWatcher;
13 | import android.util.Log;
14 | import android.view.View;
15 | import android.view.Window;
16 | import android.view.WindowManager;
17 | import android.view.animation.Animation;
18 | import android.view.animation.ScaleAnimation;
19 | import android.widget.Button;
20 | import android.widget.EditText;
21 | import android.widget.ImageView;
22 | import android.widget.TextView;
23 |
24 | import com.shawn.fakewechat.adapter.ChatAdapter;
25 | import com.shawn.fakewechat.bean.MsgData;
26 | import com.shawn.fakewechat.utils.HelpUtils;
27 | import com.readystatesoftware.systembartint.SystemBarTintManager;
28 |
29 | import java.util.ArrayList;
30 | import java.util.List;
31 | import java.util.Random;
32 |
33 |
34 | /**
35 | * @author shawn 小烦 2017/8/9.
36 | */
37 |
38 | public class ChatActivity extends AppCompatActivity {
39 |
40 | public final static int TYPE_RECEIVER_MSG = 0x21;
41 | public final static int TYPE_SENDER_MSG = 0x22;
42 | public final static int TYPE_TIME_STAMP = 0x23;
43 |
44 | private int profileId = R.drawable.hdimg_3;
45 |
46 | @Override
47 | protected void onCreate(@Nullable Bundle savedInstanceState) {
48 | super.onCreate(savedInstanceState);
49 | setContentView(R.layout.activity_wechat_chat);
50 |
51 | HelpUtils.transparentNav(this);
52 | Toolbar bar = findViewById(R.id.activity_wechat_chat_toolbar);
53 | setSupportActionBar(bar);
54 | getSupportActionBar().setTitle("");
55 | TextView tv = findViewById(R.id.activity_wechat_chat_tv_name);
56 | String name = getIntent().getStringExtra("name");
57 | profileId = getIntent().getIntExtra("profileId", R.drawable.hdimg_3);
58 | if (name != null) {
59 | tv.setText(name);
60 | }
61 | initComponents();
62 | }
63 |
64 | private void initComponents() {
65 | ImageView iv_back = findViewById(R.id.activity_wechat_chat_back);
66 | TextView tv_user = findViewById(R.id.activity_wechat_chat_tv_name);
67 | ImageView iv_voice = findViewById(R.id.activity_wechat_chat_iv_voice);
68 | EditText et_msg = findViewById(R.id.activity_wechat_chat_et_msg);
69 | ImageView iv_emoji = findViewById(R.id.activity_wechat_chat_iv_emoji);
70 | ImageView iv_add = findViewById(R.id.activity_wechat_chat_iv_add);
71 | Button btn_send = findViewById(R.id.activity_wechat_chat_btn_send);
72 | RecyclerView rv = findViewById(R.id.activity_wechat_chat_rv);
73 | rv.setLayoutManager(new LinearLayoutManager(this));
74 |
75 |
76 | iv_back.setOnClickListener((v) -> finish());
77 | btn_send.startAnimation(getVisibleAnim(false, btn_send));
78 | btn_send.setVisibility(View.GONE);
79 |
80 |
81 | et_msg.addTextChangedListener(new TextWatcher() {
82 | @Override
83 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
84 |
85 | }
86 |
87 | @Override
88 | public void onTextChanged(CharSequence s, int start, int before, int count) {
89 | Log.i("tag", "onTextChanged --- start -> " + start + " , count ->" + count + ",before ->" + before);
90 | if (start == 0 && count > 0) {
91 | btn_send.startAnimation(getVisibleAnim(true, btn_send));
92 | btn_send.setVisibility(View.VISIBLE);
93 | iv_add.setVisibility(View.GONE);
94 | }
95 |
96 | if (start == 0 && count == 0) {
97 | //btn_send.startAnimation(getVisibleAnim(false, btn_send));
98 | btn_send.setVisibility(View.GONE);
99 | iv_add.startAnimation(getVisibleAnim(true, iv_add));
100 | iv_add.setVisibility(View.VISIBLE);
101 | }
102 | }
103 |
104 | @Override
105 | public void afterTextChanged(Editable s) {
106 |
107 | }
108 | });
109 |
110 |
111 | String[] msgs = {"在吗", "不在", "不在怎么回消息的", "我是天才机器人", "机器人这么智能啊", "是啊,时代在发展", "那你唱歌看看"
112 | , "你叫唱就唱啊,多没面子", "那你能干嘛", "啥都不能", "那你有啥用?", "没啥用你和我聊啥", "我想看下有到底有啥用",
113 | "你这智商看不出来的", "你智商能看出来啥?", "你智商不足", "不带你这么聊天的...", "那你说应该怎么聊天才对?",
114 | "没啥对不对就是瞎聊", "看到一条新闻\"43岁男友不回家带饭 27岁女友放火点房子涉刑罪\" , 好逗!!!", "哈哈哈哈~~~"};
115 | List data = new ArrayList<>();
116 |
117 | for (int i = 0; i < msgs.length; i++) {
118 | MsgData msgData = new MsgData(msgs[i], HelpUtils.getCurrentMillisTime(), i % 2 == 0 ? profileId : R.drawable.hdimg_1
119 | , i % 2 == 0 ? TYPE_RECEIVER_MSG : TYPE_SENDER_MSG);
120 | data.add(i, msgData);
121 | }
122 |
123 | ChatAdapter adapter = new ChatAdapter(this, data);
124 | rv.setAdapter(adapter);
125 | Handler smartReplyMsgHandler = new Handler();
126 |
127 |
128 | btn_send.setOnClickListener((v) -> {
129 | String sendMsg = et_msg.getText().toString();
130 | MsgData msgData = new MsgData(sendMsg, HelpUtils.getCurrentMillisTime(), R.drawable.hdimg_1, TYPE_SENDER_MSG);
131 | data.add(data.size(), msgData);
132 | adapter.notifyDataSetChanged();
133 | rv.scrollToPosition(data.size() - 1);
134 | et_msg.setText("");
135 |
136 | smartReplyMsgHandler.postDelayed(() -> {
137 | String receiveMsg = getRandomMessage();
138 | MsgData msgData1 = new MsgData(receiveMsg, HelpUtils.getCurrentMillisTime(), profileId, TYPE_RECEIVER_MSG);
139 | data.add(data.size(), msgData1);
140 | adapter.notifyDataSetChanged();
141 | rv.scrollToPosition(data.size() - 1);
142 | smartReplyMsgHandler.removeCallbacksAndMessages(null);
143 | }, 500);
144 |
145 |
146 | });
147 | }
148 |
149 |
150 | private String getRandomMessage() {
151 | String[] randomMsgs = {"我是机器人", "你发再多我主人也看不到", "不要回了", "你好无聊啊", "其实你发的没点用,我会全部把它过滤掉"
152 | , "因为我是机器人", "不管你信不信,反正我是不信", "再发我就神经错乱了", "我无法正常跟你沟通", "你说的我懂,我就是不做", "哈哈哈~~~"
153 | , "嘻嘻", "呵呵", "你可以走了", "我没逻辑的不要奢求多了", "我就呵呵了", "什么鬼", "我不懂", "啥意思?", "好了,你可以退下了", "本机器宝宝累了"};
154 | Random random = new Random();
155 | int pos = random.nextInt(randomMsgs.length);
156 | if (pos >= 0 && pos < randomMsgs.length - 1)
157 | return randomMsgs[pos];
158 | else
159 | return randomMsgs[0];
160 | }
161 |
162 |
163 | private Animation getVisibleAnim(boolean show, View view) {
164 | view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
165 | int y = view.getMeasuredHeight() / 4;
166 | int x = view.getMeasuredWidth() / 4;
167 | if (show) {
168 | ScaleAnimation showAnim = new ScaleAnimation(0.01f, 1f, 0.01f, 1f, x, y);
169 | showAnim.setDuration(200);
170 | return showAnim;
171 |
172 | } else {
173 |
174 | ScaleAnimation hiddenAnim = new ScaleAnimation(1f, 0.01f, 1f, 0.01f, x, y);
175 | hiddenAnim.setDuration(200);
176 | return hiddenAnim;
177 | }
178 | }
179 |
180 | }
181 |
--------------------------------------------------------------------------------
/app/src/main/java/com/shawn/fakewechat/WechatActivity.java:
--------------------------------------------------------------------------------
1 | package com.shawn.fakewechat;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.os.Handler;
6 | import android.support.annotation.Nullable;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.support.v7.widget.Toolbar;
9 | import android.view.Gravity;
10 | import android.view.MotionEvent;
11 | import android.view.View;
12 | import android.widget.ListView;
13 |
14 | import java.util.ArrayList;
15 | import java.util.Arrays;
16 | import java.util.LinkedList;
17 | import java.util.List;
18 |
19 | import com.shawn.fakewechat.adapter.ContactAdapter;
20 | import com.shawn.fakewechat.adapter.SimpleMenuAdapter;
21 | import com.shawn.fakewechat.component.PopupMenuWindows;
22 | import com.shawn.fakewechat.bean.ContactShowInfo;
23 | import com.shawn.fakewechat.utils.HelpUtils;
24 |
25 | /**
26 | * Created by fengshawn on 2017/8/1.
27 | */
28 |
29 | public class WechatActivity extends AppCompatActivity {
30 |
31 | public static final int TYPE_USER = 0x11;
32 | public static final int TYPE_SERVICE = 0X12;
33 | public static final int TYPE_SUBSCRIBE = 0x13;
34 | private int toolbarHeight, statusBarHeight;
35 |
36 | private Handler mHandler = new Handler();
37 |
38 |
39 | @Override
40 | protected void onCreate(@Nullable Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 | setContentView(R.layout.activity_wechat_main);
43 |
44 | HelpUtils.transparentNav(this);
45 |
46 | Toolbar bar = findViewById(R.id.activity_wechat_toolbar);
47 | setSupportActionBar(bar);
48 | getSupportActionBar().setTitle("");
49 | initData();
50 |
51 | bar.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
52 | toolbarHeight = bar.getMeasuredHeight();
53 | statusBarHeight = HelpUtils.getStatusBarHeight(WechatActivity.this);
54 | }
55 |
56 | private void initData() {
57 | ListView lv = findViewById(R.id.activity_wechat_lv);
58 | int[] headImgRes = {R.drawable.hdimg_3, R.drawable.group1, R.drawable.hdimg_2, R.drawable.user_2,
59 | R.drawable.user_3, R.drawable.user_4, R.drawable.user_5, R.drawable.hdimg_4,
60 | R.drawable.hdimg_5, R.drawable.hdimg_6};
61 |
62 | String[] usernames = {"Fiona", " ... ", "冯小", "深圳社保", "服务通知", "招商银行信用卡",
63 | "箫景、Fiona", "吴晓晓", "肖箫", "唐小晓"};
64 | //最新消息
65 | String[] lastMsgs = {"我看看", "吴晓晓:无人超市啊", "最近在忙些什么", "八月一号猛料,内地社保在这2...",
66 | "微信支付凭证", "#今日签到#你能到大的,比想象...", "箫景:准备去哪嗨", "[Video Call]", "什么东西?", "[微信红包]"};
67 |
68 | String[] lastMsgTimes = {"17:40", "10:56", "7月26日", "昨天", "7月27日", "09:46",
69 | "7月18日", "星期一", "7月26日", "4月23日"};
70 |
71 | int[] types = {TYPE_USER, TYPE_USER, TYPE_USER, TYPE_SUBSCRIBE, TYPE_SERVICE, TYPE_SUBSCRIBE,
72 | TYPE_USER, TYPE_USER, TYPE_USER, TYPE_USER};
73 | //静音&已读
74 | boolean[] isMutes = {false, true, false, false, false, false, true, false, false, false};
75 | boolean[] isReads = {true, true, true, true, true, true, true, true, true, true};
76 |
77 | List infos = new LinkedList<>();
78 |
79 | for (int i = 0; i < headImgRes.length; i++) {
80 | infos.add(i, new ContactShowInfo(headImgRes[i], usernames[i], lastMsgs[i], lastMsgTimes[i], isMutes[i], isReads[i], types[i]));
81 | }
82 | ContactAdapter adapter = new ContactAdapter(this, R.layout.item_wechat_main, infos);
83 | lv.setAdapter(adapter);
84 |
85 |
86 | lv.setOnTouchListener(new View.OnTouchListener() {
87 | int preX, preY;
88 | boolean isSlip = false, isLongClick = false;
89 |
90 | @Override
91 | public boolean onTouch(View v, MotionEvent event) {
92 | switch (event.getAction()) {
93 | case MotionEvent.ACTION_DOWN:
94 | preX = (int) event.getX();
95 | preY = (int) event.getY();
96 | mHandler.postDelayed(() -> {
97 | isLongClick = true;
98 | int x = (int) event.getX();
99 | int y = (int) event.getY();
100 | //延时500ms后,其Y的坐标加入了Toolbar和statusBar高度
101 | int position = lv.pointToPosition(x, y - toolbarHeight - statusBarHeight);
102 | initPopupMenu(v, x, y, adapter, position, infos);
103 |
104 | }, 500);
105 | break;
106 |
107 | case MotionEvent.ACTION_MOVE:
108 | int nowX = (int) event.getX();
109 | int nowY = (int) event.getY();
110 |
111 | int movedX = Math.abs(nowX - preX);
112 | int movedY = Math.abs(nowY - preY);
113 | if (movedX > 50 || movedY > 50) {
114 | isSlip = true;
115 | mHandler.removeCallbacksAndMessages(null);
116 | //处理滑动事件
117 | }
118 | break;
119 |
120 |
121 | case MotionEvent.ACTION_UP:
122 | mHandler.removeCallbacksAndMessages(null);
123 | if (!isSlip && !isLongClick) {
124 | //处理单击事件
125 | int position = lv.pointToPosition(preX, preY);
126 |
127 | Intent intent = new Intent(WechatActivity.this, ChatActivity.class);
128 | intent.putExtra("name", usernames[position]);
129 | intent.putExtra("profileId", headImgRes[position]);
130 | startActivity(intent);
131 | } else {
132 | isSlip = false;
133 | isLongClick = false;
134 | }
135 | break;
136 | }
137 | return false;
138 | }
139 | });
140 | }
141 |
142 | /**
143 | * 设置已读还是未读
144 | *
145 | * @param isRead true已读,false未读
146 | * @param position item position
147 | * @param adapter 数据源
148 | * @param datas
149 | */
150 | private void setIsRead(boolean isRead, int position, ContactAdapter adapter, List datas) {
151 | ContactShowInfo info = datas.get(position);
152 | info.setRead(isRead);
153 | adapter.notifyDataSetChanged();
154 | }
155 |
156 | /**
157 | * 删除指定位置item
158 | *
159 | * @param position 指定删除position
160 | * @param adapter 数据源
161 | * @param datas
162 | */
163 | private void deleteMsg(int position, ContactAdapter adapter, List datas) {
164 | datas.remove(position);
165 | adapter.notifyDataSetChanged();
166 | }
167 |
168 | /**
169 | * 初始化popup菜单
170 | */
171 | private void initPopupMenu(View anchorView, int posX, int posY, ContactAdapter adapter, int itemPos, List data) {
172 | List list = new ArrayList<>();
173 | ContactShowInfo showInfo = data.get(itemPos);
174 | //初始化弹出菜单项
175 | switch (showInfo.getAccountType()) {
176 | case TYPE_SERVICE:
177 | list.clear();
178 | if (showInfo.isRead())
179 | list.add("标为未读");
180 | else
181 | list.add("标为已读");
182 | list.add("删除该聊天");
183 | break;
184 |
185 | case TYPE_SUBSCRIBE:
186 | list.clear();
187 | if (showInfo.isRead())
188 | list.add("标为未读");
189 | else
190 | list.add("标为已读");
191 | list.add("置顶公众号");
192 | list.add("取消关注");
193 | list.add("删除该聊天");
194 | break;
195 |
196 | case TYPE_USER:
197 | list.clear();
198 | if (showInfo.isRead())
199 | list.add("标为未读");
200 | else
201 | list.add("标为已读");
202 | list.add("置顶聊天");
203 | list.add("删除该聊天");
204 | break;
205 | }
206 | SimpleMenuAdapter menuAdapter = new SimpleMenuAdapter<>(this, R.layout.item_menu, list);
207 | PopupMenuWindows ppm = new PopupMenuWindows(this, R.layout.popup_menu_general_layout, menuAdapter);
208 | int[] posArr = ppm.reckonPopWindowShowPos(posX, posY);
209 | ppm.setAutoFitStyle(true);
210 | ppm.setOnMenuItemClickListener((parent, view, position, id) -> {
211 |
212 | switch (list.get(position)) {
213 | case "标为未读":
214 | setIsRead(false, itemPos, adapter, data);
215 | break;
216 |
217 | case "标为已读":
218 | setIsRead(true, itemPos, adapter, data);
219 | break;
220 |
221 | case "置顶聊天":
222 | case "置顶公众号":
223 | stickyTop(adapter, data, itemPos);
224 | break;
225 |
226 | case "取消关注":
227 | case "删除该聊天":
228 | deleteMsg(itemPos, adapter, data);
229 | break;
230 | }
231 | ppm.dismiss();
232 | });
233 | ppm.showAtLocation(anchorView, Gravity.NO_GRAVITY, posArr[0], posArr[1]);
234 | }
235 |
236 |
237 | /**
238 | * 置顶item
239 | *
240 | * @param adapter
241 | * @param datas
242 | */
243 | private void stickyTop(ContactAdapter adapter, List datas, int position) {
244 | if (position > 0) {
245 | ContactShowInfo stickyTopItem = datas.get(position);
246 | datas.remove(position);
247 | datas.add(0, stickyTopItem);
248 | } else {
249 | return;
250 | }
251 | adapter.notifyDataSetChanged();
252 | }
253 |
254 | }
255 |
256 |
--------------------------------------------------------------------------------