├── 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 | ![image](https://github.com/fx4758/FakeWechat/blob/master/image/1.png?raw=false) 18 | ![image](https://github.com/fx4758/FakeWechat/blob/master/image/2.png?raw=false) 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 | 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 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | 38 | 43 | 44 | 45 | 46 | 47 | 48 | 1.8 49 | 50 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 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 |