├── LICENSE ├── README.md └── Trunk ├── .gitignore ├── app ├── .gitignore ├── build.gradle ├── libs │ ├── EasyNet │ │ ├── EasyNet.jar │ │ └── EasyNet_lib │ │ │ └── fastjson-1.2.73.jar │ └── XposedBridgeApi-82.jar ├── proguard-rules.pro ├── release │ ├── FModule_1_0.apk │ ├── FModule_1_1.apk │ ├── FModule_1_2.apk │ ├── FModule_1_2_1.apk │ └── output-metadata.json └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── fmodule │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── xposed_init │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── fmodule │ │ │ ├── AboutActivity.java │ │ │ ├── ContactProfileActivity.java │ │ │ ├── ContactsActivity.java │ │ │ ├── ContactsListAdapter.java │ │ │ ├── ContactsSearchActivity.java │ │ │ ├── ContactsSearchListAdapter.java │ │ │ ├── DialogLoading.java │ │ │ ├── DiceActivity.java │ │ │ ├── GlobalReplyActivity.java │ │ │ ├── GlobalSendActivity.java │ │ │ ├── MHandler.java │ │ │ ├── MService.java │ │ │ ├── MainActivity.java │ │ │ ├── ReplyActivity.java │ │ │ ├── ReplyListAdapter.java │ │ │ ├── ReplySettingsActivity.java │ │ │ ├── RoundImageView.java │ │ │ ├── SendActivity.java │ │ │ ├── SendListAdapter.java │ │ │ ├── SendSettingsActivity.java │ │ │ ├── VoiceLenActivity.java │ │ │ ├── WXListAdapter.java │ │ │ ├── eventsystem │ │ │ ├── EventHandler.java │ │ │ ├── EventHandlerProxy.java │ │ │ ├── EventIdType.java │ │ │ └── EventSystem.java │ │ │ ├── hooks │ │ │ ├── AvatarGetter.java │ │ │ ├── ContactsGetter.java │ │ │ ├── ContactsHook.java │ │ │ ├── DecryptStrCallback.java │ │ │ ├── DiceHooker.java │ │ │ ├── EmojiSender.java │ │ │ ├── HookDemo.java │ │ │ ├── HookEntryUtil.java │ │ │ ├── MessageSender.java │ │ │ ├── PayeeQRCodeHook.java │ │ │ ├── ReceiveMessageHook.java │ │ │ ├── ReceiveMsgHooker.java │ │ │ ├── SendEmojiHook.java │ │ │ ├── SendMessageHook.java │ │ │ ├── SendVoiceHook.java │ │ │ ├── UserInfoGetter.java │ │ │ ├── UserInfoHook.java │ │ │ ├── VoiceHooker.java │ │ │ ├── WXMessageUtil.java │ │ │ ├── WXParasite.java │ │ │ └── models │ │ │ │ ├── RecvModel.java │ │ │ │ ├── SendItemModel.java │ │ │ │ ├── SendModel.java │ │ │ │ └── TalkModel.java │ │ │ ├── hooktask │ │ │ ├── ContactReplyTask.java │ │ │ ├── ContactSendTask.java │ │ │ ├── DiceTask.java │ │ │ ├── HookTask.java │ │ │ ├── HookTaskHelper.java │ │ │ ├── ReplyTask.java │ │ │ ├── SendTask.java │ │ │ └── VoiceLenTask.java │ │ │ ├── message │ │ │ ├── MessageManifest.java │ │ │ ├── TestMessage.java │ │ │ ├── contacts │ │ │ │ ├── PContacts.java │ │ │ │ └── QContacts.java │ │ │ ├── hooktask │ │ │ │ ├── MDiceTask.java │ │ │ │ ├── MGlobalReplyTasks.java │ │ │ │ ├── MHookTask.java │ │ │ │ ├── MReplyTask.java │ │ │ │ ├── MSendTask.java │ │ │ │ └── MVoiceLenTask.java │ │ │ ├── identify │ │ │ │ ├── PIdentify.java │ │ │ │ └── QIdentify.java │ │ │ ├── useravatar │ │ │ │ ├── PUserAvatar.java │ │ │ │ └── QUserAvatar.java │ │ │ └── wxuserinfo │ │ │ │ ├── PWXUserInfo.java │ │ │ │ └── QWXUserInfo.java │ │ │ ├── other │ │ │ ├── ContactData.java │ │ │ └── NetHelper.java │ │ │ └── sqlite │ │ │ └── SQLiteHelper.java │ └── res │ │ ├── anim │ │ ├── dialog_enter.xml │ │ ├── dialog_exit.xml │ │ └── rotate_animation.xml │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── activity_action_bar_shape.xml │ │ ├── activity_send_settings_select_contacts_btn_shape.xml │ │ ├── activity_send_settings_select_contacts_text_shape.xml │ │ ├── avatar.jpg │ │ ├── dialog_loading.xml │ │ ├── ghost_white_bg_shape.xml │ │ ├── ic_launcher_background.xml │ │ ├── icon_add.png │ │ ├── icon_add_primary.png │ │ ├── icon_delete.png │ │ ├── icon_edit.png │ │ ├── icon_edit1.png │ │ ├── icon_menu.png │ │ ├── icon_search.png │ │ ├── loading_bg.9.png │ │ ├── popup_bg.9.png │ │ ├── primary_bg_circle_shape.xml │ │ ├── search_bar_shape.xml │ │ └── white_bg_circle_shape.xml │ │ ├── layout │ │ ├── activity_about.xml │ │ ├── activity_contacts.xml │ │ ├── activity_contacts_list_item.xml │ │ ├── activity_contacts_profile.xml │ │ ├── activity_contacts_search.xml │ │ ├── activity_contacts_search_list_item.xml │ │ ├── activity_dice.xml │ │ ├── activity_global_reply.xml │ │ ├── activity_global_send.xml │ │ ├── activity_main.xml │ │ ├── activity_main_wxlist_item.xml │ │ ├── activity_reply.xml │ │ ├── activity_reply_replylist_item.xml │ │ ├── activity_reply_settings.xml │ │ ├── activity_send.xml │ │ ├── activity_send_sendlist_item.xml │ │ ├── activity_send_settings.xml │ │ ├── activity_voice_len.xml │ │ ├── custom_action_bar.xml │ │ └── dialog_loading.xml │ │ ├── menu │ │ ├── activity_contacts.xml │ │ ├── activity_global_reply.xml │ │ ├── activity_global_send.xml │ │ ├── activity_main.xml │ │ ├── activity_reply_msg.xml │ │ └── activity_send.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── 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 │ │ └── loading_icon.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── fmodule │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── pickdatetime ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── pickdatetime │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── pickdatetime │ │ │ ├── DatePickDialog.java │ │ │ ├── DateTimePickerView.java │ │ │ ├── OnChangeListener.java │ │ │ ├── OnSureListener.java │ │ │ ├── adapter │ │ │ ├── BaseWheelAdapter.java │ │ │ ├── GeneralWheelAdapter.java │ │ │ ├── WheelAdapter.java │ │ │ └── datetime │ │ │ │ ├── DatePickAdapter.java │ │ │ │ ├── DayAdapter.java │ │ │ │ ├── HourAdapter.java │ │ │ │ ├── MinuteAdapter.java │ │ │ │ ├── MonthAdapter.java │ │ │ │ └── YearAdapter.java │ │ │ ├── bean │ │ │ ├── DateParams.java │ │ │ └── DatePick.java │ │ │ └── view │ │ │ ├── ItemsRange.java │ │ │ ├── OnWheelChangedListener.java │ │ │ ├── OnWheelClickedListener.java │ │ │ ├── OnWheelScrollListener.java │ │ │ ├── WheelRecycle.java │ │ │ ├── WheelScroller.java │ │ │ └── WheelView.java │ └── res │ │ ├── layout │ │ ├── cbk_dialog_pick_time.xml │ │ └── cbk_wheel_default_inner_text.xml │ │ └── values │ │ ├── strings.xml │ │ └── style.xml │ └── test │ └── java │ └── com │ └── example │ └── pickdatetime │ └── ExampleUnitTest.java └── settings.gradle /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 芳辉啊 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FModule 2 | # 基于Xposed的微信助手,可以定时发消息表情图片、自动回复等 3 | # 微信版本要求7.0.17 4 | # 什么获取通讯录、头像、消息、自己看 5 | ![20201002232818150](https://user-images.githubusercontent.com/34017395/110879518-19c92b00-8318-11eb-9901-8193ee4d9ac8.png) 6 | ![20201002232859673](https://user-images.githubusercontent.com/34017395/110879533-2057a280-8318-11eb-938d-220d3563bc25.png) 7 | ![20201002232957320](https://user-images.githubusercontent.com/34017395/110879560-28afdd80-8318-11eb-99ac-3aeafcbbbfb8.png) 8 | ![2020100223293732](https://user-images.githubusercontent.com/34017395/110879568-2c436480-8318-11eb-8b2b-137918c569d1.png) 9 | ![20201002233107478](https://user-images.githubusercontent.com/34017395/110879571-2ea5be80-8318-11eb-8389-dc0a2f07d30a.png) 10 | ![20201002233136547](https://user-images.githubusercontent.com/34017395/110879577-306f8200-8318-11eb-8745-4a592999ad3c.png) 11 | -------------------------------------------------------------------------------- /Trunk/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /Trunk/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /Trunk/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "29.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.example.fmodule" 9 | minSdkVersion 22 10 | targetSdkVersion 29 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled true 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | } 28 | 29 | dependencies { 30 | //implementation fileTree(dir: "libs", include: ["*.jar"]) 31 | implementation 'androidx.appcompat:appcompat:1.2.0' 32 | implementation 'androidx.constraintlayout:constraintlayout:2.0.1' 33 | implementation project(path: ':pickdatetime') 34 | implementation files('libs\\EasyNet\\EasyNet.jar') 35 | implementation files('libs\\EasyNet\\EasyNet_lib\\fastjson-1.2.73.jar') 36 | compileOnly files('libs\\XposedBridgeApi-82.jar') 37 | testImplementation 'junit:junit:4.12' 38 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 39 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 40 | implementation "com.squareup.okhttp3:okhttp:3.10.0" 41 | } -------------------------------------------------------------------------------- /Trunk/app/libs/EasyNet/EasyNet.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/libs/EasyNet/EasyNet.jar -------------------------------------------------------------------------------- /Trunk/app/libs/EasyNet/EasyNet_lib/fastjson-1.2.73.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/libs/EasyNet/EasyNet_lib/fastjson-1.2.73.jar -------------------------------------------------------------------------------- /Trunk/app/libs/XposedBridgeApi-82.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/libs/XposedBridgeApi-82.jar -------------------------------------------------------------------------------- /Trunk/app/release/FModule_1_0.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/release/FModule_1_0.apk -------------------------------------------------------------------------------- /Trunk/app/release/FModule_1_1.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/release/FModule_1_1.apk -------------------------------------------------------------------------------- /Trunk/app/release/FModule_1_2.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/release/FModule_1_2.apk -------------------------------------------------------------------------------- /Trunk/app/release/FModule_1_2_1.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/release/FModule_1_2_1.apk -------------------------------------------------------------------------------- /Trunk/app/release/output-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "artifactType": { 4 | "type": "APK", 5 | "kind": "Directory" 6 | }, 7 | "applicationId": "com.example.fmodule", 8 | "variantName": "release", 9 | "elements": [ 10 | { 11 | "type": "SINGLE", 12 | "filters": [], 13 | "properties": [], 14 | "versionCode": 1, 15 | "versionName": "1.0", 16 | "enabled": true, 17 | "outputFile": "app-release.apk" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /Trunk/app/src/androidTest/java/com/example/fmodule/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("com.example.fmodule", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /Trunk/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 36 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Trunk/app/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | com.example.fmodule.hooks.WXParasite -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/AboutActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | 5 | import android.os.Bundle; 6 | 7 | public class AboutActivity extends AppCompatActivity { 8 | 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.activity_about); 13 | } 14 | } -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/ContactsListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Bitmap; 6 | import android.graphics.BitmapFactory; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.BaseAdapter; 11 | import android.widget.ImageView; 12 | import android.widget.TextView; 13 | 14 | import com.example.fmodule.message.useravatar.PUserAvatar; 15 | import com.example.fmodule.message.useravatar.QUserAvatar; 16 | import com.example.fmodule.other.ContactData; 17 | import com.example.fmodule.other.NetHelper; 18 | 19 | import java.util.HashMap; 20 | import java.util.Hashtable; 21 | import java.util.LinkedList; 22 | 23 | import easynet.network.Session; 24 | 25 | public class ContactsListAdapter extends BaseAdapter { 26 | 27 | private Context context; 28 | private String wxId; 29 | private LinkedList contacts; 30 | private LayoutInflater inflater; 31 | private final Hashtable avatarCache = new Hashtable<>(); 32 | private Session session; 33 | 34 | public ContactsListAdapter(Context context, String wxId, LinkedList contacts, Session session) { 35 | this.context = context; 36 | this.wxId = wxId; 37 | this.contacts = contacts; 38 | this.inflater = LayoutInflater.from(context); 39 | this.session = session; 40 | } 41 | 42 | @Override 43 | public int getCount() { 44 | return contacts.size(); 45 | } 46 | 47 | @Override 48 | public Object getItem(int position) { 49 | return contacts.get(position); 50 | } 51 | 52 | @Override 53 | public long getItemId(int position) { 54 | return position; 55 | } 56 | 57 | @Override 58 | public View getView(int position, View convertView, ViewGroup parent) { 59 | ContactData data = (ContactData)getItem(position); 60 | ViewHolder holder = null; 61 | if (convertView == null) { 62 | convertView = inflater.inflate(R.layout.activity_contacts_list_item, null); 63 | holder = new ViewHolder(); 64 | holder.avatarImage = convertView.findViewById(R.id.avatarImage); 65 | holder.nicknameText = convertView.findViewById(R.id.nicknameText); 66 | holder.usernameText = convertView.findViewById(R.id.wxIdText); 67 | convertView.setTag(holder); 68 | } else { 69 | holder = (ViewHolder) convertView.getTag(); 70 | } 71 | holder.username = data.username; 72 | String nickname = data.conRemark.isEmpty() ? data.nickname : data.conRemark; 73 | holder.nicknameText.setText(nickname); 74 | holder.usernameText.setText("wxid_tit23hifaf"); 75 | setAvatar(holder); 76 | return convertView; 77 | } 78 | 79 | private class ViewHolder { 80 | public String username; 81 | public ImageView avatarImage; 82 | public TextView nicknameText; 83 | public TextView usernameText; 84 | } 85 | 86 | private void setAvatar(ViewHolder holder) { 87 | String username = holder.username; 88 | Bitmap bitmap = avatarCache.get(username); 89 | if (bitmap != null) { 90 | holder.avatarImage.setImageBitmap(bitmap); 91 | return; 92 | } 93 | new Thread(new Runnable() { 94 | @Override 95 | public void run() { 96 | QUserAvatar qUserAvatar = new QUserAvatar(); 97 | qUserAvatar.wxId = username; 98 | PUserAvatar pUserAvatar = (PUserAvatar) session.call(qUserAvatar).execute(); 99 | if (pUserAvatar == null) { 100 | return; 101 | } 102 | byte[] bytes = pUserAvatar.avatarData; 103 | if (bytes == null) { 104 | pUserAvatar = (PUserAvatar) session.call(qUserAvatar).execute(); 105 | if (pUserAvatar == null) { 106 | return; 107 | } 108 | bytes = pUserAvatar.avatarData; 109 | if (bytes == null) { 110 | return; 111 | } 112 | } 113 | Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); 114 | avatarCache.put(username, bmp); 115 | 116 | ((Activity)context).runOnUiThread(new Runnable() { 117 | @Override 118 | public void run() { 119 | if (holder.username == username) { 120 | holder.avatarImage.setImageBitmap(bmp); 121 | } 122 | } 123 | }); 124 | } 125 | }).start(); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/ContactsSearchListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.BaseAdapter; 9 | import android.widget.CheckBox; 10 | import android.widget.CompoundButton; 11 | import android.widget.ImageView; 12 | import android.widget.TextView; 13 | 14 | import com.example.fmodule.other.ContactData; 15 | 16 | import java.util.ArrayList; 17 | import java.util.HashSet; 18 | import java.util.LinkedList; 19 | 20 | public class ContactsSearchListAdapter extends BaseAdapter { 21 | private final String TAG = "ContactsSearchListAdapter"; 22 | private Context context; 23 | private LayoutInflater inflater; 24 | private boolean multiple; 25 | private ArrayList listData; 26 | private LinkedList selectedList; 27 | private LinkedList noneSelectedList; 28 | 29 | public ContactsSearchListAdapter(Context context, ArrayList listData) { 30 | if (listData == null) { 31 | throw new RuntimeException(TAG + ": 数据源不允许为空"); 32 | } 33 | this.context = context; 34 | this.inflater = LayoutInflater.from(context); 35 | this.multiple = false; 36 | this.listData = listData; 37 | this.selectedList = null; 38 | this.noneSelectedList = null; 39 | } 40 | 41 | public ContactsSearchListAdapter(Context context, ArrayList listData, LinkedList selectedList, LinkedList noneSelectedList) { 42 | if (listData == null || selectedList == null || noneSelectedList == null) { 43 | throw new RuntimeException(TAG + ": 数据源不允许为空"); 44 | } 45 | this.context = context; 46 | this.inflater = LayoutInflater.from(context); 47 | this.multiple = true; 48 | this.listData = listData; 49 | this.selectedList = selectedList; 50 | this.noneSelectedList = noneSelectedList; 51 | } 52 | 53 | @Override 54 | public int getCount() { 55 | return listData.size(); 56 | } 57 | 58 | @Override 59 | public ContactData getItem(int position) { 60 | return listData.get(position); 61 | } 62 | 63 | @Override 64 | public long getItemId(int position) { 65 | return position; 66 | } 67 | 68 | @Override 69 | public View getView(int position, View convertView, ViewGroup parent) { 70 | ContactData data = getItem(position); 71 | ViewHolder holder = null; 72 | if (convertView == null) { 73 | convertView = inflater.inflate(R.layout.activity_contacts_search_list_item, null); 74 | holder = new ViewHolder(); 75 | holder.avatarImage = convertView.findViewById(R.id.avatarImage); 76 | holder.nicknameText = convertView.findViewById(R.id.nicknameText); 77 | holder.usernameText = convertView.findViewById(R.id.wxIdText); 78 | holder.checkBox = convertView.findViewById(R.id.checkBox); 79 | if (multiple) { 80 | holder.checkBox.setVisibility(View.VISIBLE); 81 | holder.onCheckedChangeListener = new OnCheckedChangeListener(); 82 | } else { 83 | holder.checkBox.setVisibility(View.INVISIBLE); 84 | } 85 | 86 | convertView.setTag(holder); 87 | } else { 88 | holder = (ViewHolder) convertView.getTag(); 89 | } 90 | if (multiple) { 91 | holder.checkBox.setOnCheckedChangeListener(null); 92 | holder.checkBox.setChecked(selectedList.contains(data)); 93 | holder.onCheckedChangeListener.data = data; 94 | holder.checkBox.setOnCheckedChangeListener(holder.onCheckedChangeListener); 95 | } 96 | 97 | String nickname = data.conRemark.isEmpty() ? data.nickname : data.conRemark; 98 | holder.nicknameText.setText(nickname); 99 | holder.usernameText.setText("wxid_sldk239sti"); 100 | return convertView; 101 | } 102 | 103 | public class OnCheckedChangeListener implements CompoundButton.OnCheckedChangeListener { 104 | public ContactData data; 105 | @Override 106 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 107 | if (isChecked) { 108 | selectedList.add(data); 109 | noneSelectedList.remove(data); 110 | }else { 111 | selectedList.remove(data); 112 | noneSelectedList.add(data); 113 | } 114 | } 115 | }; 116 | 117 | private class ViewHolder { 118 | public ImageView avatarImage; 119 | public TextView nicknameText; 120 | public TextView usernameText; 121 | public CheckBox checkBox; 122 | public OnCheckedChangeListener onCheckedChangeListener; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/DialogLoading.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.Window; 8 | import android.view.animation.Animation; 9 | import android.view.animation.AnimationUtils; 10 | import android.widget.ImageView; 11 | import android.widget.LinearLayout; 12 | import android.widget.TextView; 13 | 14 | public class DialogLoading { 15 | private Dialog loading(Context context, String msg) { 16 | LayoutInflater inflater = LayoutInflater.from(context); 17 | View v = inflater.inflate(R.layout.dialog_loading, null); 18 | LinearLayout layout = (LinearLayout) v.findViewById(R.id.dialog_view); 19 | 20 | ImageView spaceshipImage = (ImageView) v.findViewById(R.id.img); 21 | TextView tipTextView = (TextView) v.findViewById(R.id.tipTextView); 22 | 23 | Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(context, R.anim.rotate_animation); 24 | spaceshipImage.startAnimation(hyperspaceJumpAnimation); 25 | tipTextView.setText(msg); 26 | 27 | Dialog loadingDialog = new Dialog(context); 28 | loadingDialog.setContentView(layout); 29 | loadingDialog.setCancelable(false); 30 | loadingDialog.setCanceledOnTouchOutside(false); 31 | 32 | Window window = loadingDialog.getWindow(); 33 | // WindowManager.LayoutParams lp = window.getAttributes(); 34 | // lp.width = WindowManager.LayoutParams.MATCH_PARENT; 35 | // lp.height = WindowManager.LayoutParams.WRAP_CONTENT; 36 | // window.setGravity(Gravity.CENTER); 37 | // window.setAttributes(lp); 38 | window.setWindowAnimations(R.style.PopWindowAnimStyle); 39 | return loadingDialog; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/GlobalReplyActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule; 2 | 3 | import android.app.Service; 4 | import android.content.ComponentName; 5 | import android.content.Intent; 6 | import android.content.ServiceConnection; 7 | import android.database.sqlite.SQLiteDatabase; 8 | import android.os.Bundle; 9 | import android.os.IBinder; 10 | import android.view.Menu; 11 | import android.view.MenuItem; 12 | import android.widget.ListView; 13 | 14 | import androidx.annotation.NonNull; 15 | import androidx.annotation.Nullable; 16 | import androidx.appcompat.app.AppCompatActivity; 17 | 18 | import easynet.network.Session; 19 | 20 | public class GlobalReplyActivity extends AppCompatActivity { 21 | 22 | private String wxId; 23 | private ListView replyListView; 24 | private ReplyListAdapter listAdapter; 25 | private MService mService; 26 | private Session session; 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_global_reply); 31 | setTitle("全局回复"); 32 | Intent intent = getIntent(); 33 | wxId = intent.getStringExtra("wxId"); 34 | replyListView = findViewById(R.id.replyList); 35 | Intent serviceIntent = new Intent(this, MService.class); 36 | bindService(serviceIntent, conn, Service.BIND_AUTO_CREATE); 37 | } 38 | @Override 39 | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 40 | super.onActivityResult(requestCode, resultCode, data); 41 | if (requestCode != 1 || requestCode != 1) { 42 | return; 43 | } 44 | listAdapter.refreshData(); 45 | } 46 | @Override 47 | public boolean onCreateOptionsMenu(Menu menu) { 48 | getMenuInflater().inflate(R.menu.activity_global_reply, menu); 49 | return super.onCreateOptionsMenu(menu); 50 | } 51 | @Override 52 | public boolean onOptionsItemSelected(@NonNull MenuItem item) { 53 | if (item.getItemId() == R.id.add) { 54 | Intent intent = new Intent(this, ReplySettingsActivity.class); 55 | intent.putExtra("wxId", wxId); 56 | intent.putExtra("contactWxId", "all"); 57 | startActivityForResult(intent, 1); 58 | } 59 | return super.onOptionsItemSelected(item); 60 | } 61 | @Override 62 | protected void onDestroy() { 63 | unbindService(conn); 64 | mService = null; 65 | session.unregisterErrorCallback(errorCallback); 66 | super.onDestroy(); 67 | } 68 | //绑定服务回调 69 | private ServiceConnection conn = new ServiceConnection() { 70 | @Override 71 | public void onServiceConnected(ComponentName name, IBinder service) { 72 | MService.LocalBinder binder = (MService.LocalBinder)service; 73 | mService = binder.getService(); 74 | SQLiteDatabase db = mService.getSQLiteHelper().getWritableDatabase(); 75 | session = mService.getWxSession(wxId); 76 | session.registerErrorCallback(errorCallback); 77 | listAdapter = new ReplyListAdapter(GlobalReplyActivity.this, wxId, "all", db, session); 78 | replyListView.setAdapter(listAdapter); 79 | } 80 | 81 | @Override 82 | public void onServiceDisconnected(ComponentName name) { 83 | mService = null; 84 | } 85 | }; 86 | //session异常回调 87 | private Session.ErrorCallback errorCallback = new Session.ErrorCallback() { 88 | @Override 89 | public void run(Session session, Exception e) { 90 | runOnUiThread(new Runnable() { 91 | @Override 92 | public void run() { 93 | finish(); 94 | } 95 | }); 96 | } 97 | }; 98 | } -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/GlobalSendActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | import androidx.appcompat.app.AppCompatActivity; 6 | 7 | import android.app.Service; 8 | import android.content.ComponentName; 9 | import android.content.Intent; 10 | import android.content.ServiceConnection; 11 | import android.database.sqlite.SQLiteDatabase; 12 | import android.os.Bundle; 13 | import android.os.IBinder; 14 | import android.view.Menu; 15 | import android.view.MenuItem; 16 | import android.widget.ListView; 17 | 18 | import com.example.fmodule.eventsystem.EventHandler; 19 | import com.example.fmodule.eventsystem.EventIdType; 20 | import com.example.fmodule.eventsystem.EventSystem; 21 | 22 | import easynet.network.Session; 23 | 24 | public class GlobalSendActivity extends AppCompatActivity { 25 | 26 | private String wxId; 27 | private ListView listView; 28 | private SendListAdapter listAdapter; 29 | private MService mService; 30 | private Session session; 31 | @Override 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | setContentView(R.layout.activity_global_send); 35 | setTitle("多选发送"); 36 | Intent intent = getIntent(); 37 | wxId = intent.getStringExtra("wxId"); 38 | listView = findViewById(R.id.sendList); 39 | Intent serviceIntent = new Intent(this, MService.class); 40 | bindService(serviceIntent, conn, Service.BIND_AUTO_CREATE); 41 | EventSystem.getInstance().register(this); 42 | } 43 | @Override 44 | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 45 | super.onActivityResult(requestCode, resultCode, data); 46 | if (requestCode != 1 || requestCode != 1) { 47 | return; 48 | } 49 | listAdapter.refreshData(); 50 | } 51 | @Override 52 | public boolean onCreateOptionsMenu(Menu menu) { 53 | getMenuInflater().inflate(R.menu.activity_global_send, menu); 54 | return super.onCreateOptionsMenu(menu); 55 | } 56 | @Override 57 | public boolean onOptionsItemSelected(@NonNull MenuItem item) { 58 | if (item.getItemId() == R.id.add) { 59 | Intent intent = new Intent(this, SendSettingsActivity.class); 60 | intent.putExtra("wxId", wxId); 61 | intent.putExtra("contactWxId", "all"); 62 | intent.putExtra("multiple", true); 63 | startActivityForResult(intent, 1); 64 | } 65 | return super.onOptionsItemSelected(item); 66 | } 67 | @Override 68 | protected void onDestroy() { 69 | unbindService(conn); 70 | mService = null; 71 | session.unregisterErrorCallback(errorCallback); 72 | EventSystem.getInstance().unregister(this); 73 | super.onDestroy(); 74 | } 75 | //绑定服务回调 76 | private ServiceConnection conn = new ServiceConnection() { 77 | @Override 78 | public void onServiceConnected(ComponentName name, IBinder service) { 79 | MService.LocalBinder binder = (MService.LocalBinder)service; 80 | mService = binder.getService(); 81 | SQLiteDatabase db = mService.getSQLiteHelper().getWritableDatabase(); 82 | session = mService.getWxSession(wxId); 83 | session.registerErrorCallback(errorCallback); 84 | listAdapter = new SendListAdapter(GlobalSendActivity.this, wxId, "all", mService, db); 85 | listView.setAdapter(listAdapter); 86 | } 87 | 88 | @Override 89 | public void onServiceDisconnected(ComponentName name) { 90 | mService = null; 91 | } 92 | }; 93 | //session异常回调 94 | private Session.ErrorCallback errorCallback = new Session.ErrorCallback() { 95 | @Override 96 | public void run(Session session, Exception e) { 97 | runOnUiThread(new Runnable() { 98 | @Override 99 | public void run() { 100 | finish(); 101 | } 102 | }); 103 | } 104 | }; 105 | @EventHandler(EventIdType.sendTaskCompleted) 106 | public void sendTaskCompletedHandler(int sendTaskId) { 107 | runOnUiThread(new Runnable() { 108 | @Override 109 | public void run() { 110 | listAdapter.refreshData(); 111 | } 112 | }); 113 | } 114 | } -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/MHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule; 2 | 3 | import android.content.Context; 4 | import android.os.Handler; 5 | import android.os.Message; 6 | import android.widget.Toast; 7 | 8 | import androidx.annotation.NonNull; 9 | 10 | import java.lang.ref.WeakReference; 11 | 12 | public class MHandler extends Handler { 13 | private final String TAG = "MHandler"; 14 | private static MHandler instance; 15 | private WeakReference wc; 16 | private long currentThreadId; 17 | 18 | public MHandler(Context context) { 19 | this.wc = new WeakReference<>(context); 20 | currentThreadId = Thread.currentThread().getId(); 21 | } 22 | 23 | @Override 24 | public void handleMessage(@NonNull Message msg) { 25 | super.handleMessage(msg); 26 | 27 | } 28 | 29 | public void toast(final String content, final int duration) { 30 | final Context context = wc.get(); 31 | if (context == null) { 32 | return; 33 | } 34 | if (isCurrentThread()) { 35 | Toast.makeText(context, content, duration).show(); 36 | return; 37 | } 38 | this.post(new Runnable() { 39 | @Override 40 | public void run() { 41 | Toast.makeText(context, content, duration).show(); 42 | } 43 | }); 44 | } 45 | 46 | private boolean isCurrentThread() { 47 | return currentThreadId == Thread.currentThread().getId(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule; 2 | 3 | import android.app.Service; 4 | import android.content.ComponentName; 5 | import android.content.Intent; 6 | import android.content.ServiceConnection; 7 | import android.database.Cursor; 8 | import android.database.sqlite.SQLiteDatabase; 9 | import android.os.Bundle; 10 | import android.os.IBinder; 11 | import android.view.Menu; 12 | import android.view.MenuItem; 13 | import android.view.View; 14 | import android.widget.AdapterView; 15 | import android.widget.ListView; 16 | import android.widget.Toast; 17 | 18 | import androidx.annotation.NonNull; 19 | import androidx.appcompat.app.AppCompatActivity; 20 | import androidx.constraintlayout.widget.Group; 21 | 22 | import com.example.fmodule.eventsystem.EventHandler; 23 | import com.example.fmodule.eventsystem.EventIdType; 24 | import com.example.fmodule.eventsystem.EventSystem; 25 | import com.example.fmodule.hooktask.DiceTask; 26 | import com.example.fmodule.hooktask.HookTaskHelper; 27 | import com.example.fmodule.hooktask.ReplyTask; 28 | import com.example.fmodule.hooktask.SendTask; 29 | import com.example.fmodule.hooktask.VoiceLenTask; 30 | import com.example.fmodule.message.MessageManifest; 31 | import com.example.fmodule.message.hooktask.MDiceTask; 32 | import com.example.fmodule.message.hooktask.MReplyTask; 33 | import com.example.fmodule.message.hooktask.MVoiceLenTask; 34 | import com.example.fmodule.message.identify.PIdentify; 35 | import com.example.fmodule.message.identify.QIdentify; 36 | import com.example.fmodule.other.NetHelper; 37 | import com.example.fmodule.sqlite.SQLiteHelper; 38 | 39 | import java.lang.reflect.Method; 40 | import java.util.ArrayList; 41 | import java.util.Calendar; 42 | import java.util.Collection; 43 | import java.util.HashMap; 44 | import java.util.HashSet; 45 | import java.util.Hashtable; 46 | import java.util.List; 47 | import java.util.Map; 48 | 49 | import easynet.network.MessageHandler; 50 | import easynet.network.Network; 51 | import easynet.network.Session; 52 | 53 | public class MainActivity extends AppCompatActivity { 54 | 55 | private static MHandler mHandler; 56 | private static SQLiteHelper mSQLiteHelper; 57 | 58 | private ArrayList> infoList; 59 | private final Hashtable> sessionInfo = new Hashtable<>(); 60 | private WXListAdapter adapter; 61 | private ListView infoListView; 62 | private Group noSessionTipsGroup; 63 | private MService mService; 64 | 65 | @Override 66 | protected void onCreate(Bundle savedInstanceState) { 67 | super.onCreate(savedInstanceState); 68 | setContentView(R.layout.activity_main); 69 | setTitle("已连接"); 70 | mHandler = new MHandler(MainActivity.this); 71 | 72 | infoListView = findViewById(R.id.listView); 73 | infoListView.setOnItemClickListener(onWxListItemClickListener); 74 | noSessionTipsGroup = (Group)findViewById(R.id.noSessionTipsGroup); 75 | noSessionTipsGroup.setVisibility(View.VISIBLE); 76 | 77 | //创建绑定对象 78 | Intent intent = new Intent(this, MService.class); 79 | startService(intent); 80 | bindService(intent, conn, Service.BIND_AUTO_CREATE); 81 | } 82 | @Override 83 | public boolean onCreateOptionsMenu(Menu menu) { 84 | getMenuInflater().inflate(R.menu.activity_main, menu); 85 | return super.onCreateOptionsMenu(menu); 86 | } 87 | @Override 88 | public boolean onOptionsItemSelected(@NonNull MenuItem item) { 89 | if (item.getItemId() == R.id.about) { 90 | Intent intent = new Intent(this, AboutActivity.class); 91 | startActivity(intent); 92 | } 93 | return super.onOptionsItemSelected(item); 94 | } 95 | @Override 96 | protected void onDestroy() { 97 | EventSystem.getInstance().unregister(MainActivity.this); 98 | mService = null; 99 | unbindService(conn); 100 | super.onDestroy(); 101 | } 102 | //绑定服务 103 | private ServiceConnection conn = new ServiceConnection() { 104 | @Override 105 | public void onServiceConnected(ComponentName name, IBinder service) { 106 | MService.LocalBinder binder = (MService.LocalBinder)service; 107 | mService = binder.getService(); 108 | infoList = mService.getInfoList(); 109 | noSessionTipsGroup.setVisibility(infoList.size() == 0 ? View.VISIBLE : View.GONE); 110 | adapter = new WXListAdapter(MainActivity.this, infoList); 111 | infoListView.setAdapter(adapter); 112 | EventSystem.getInstance().register(MainActivity.this); 113 | } 114 | 115 | @Override 116 | public void onServiceDisconnected(ComponentName name) { 117 | mService = null; 118 | } 119 | }; 120 | @EventHandler(EventIdType.mServiceSessionChanged) 121 | public void mServiceSessionChangedHandler(String wxId) { 122 | runOnUiThread(new Runnable() { 123 | @Override 124 | public void run() { 125 | adapter.notifyDataSetChanged(); 126 | noSessionTipsGroup.setVisibility(infoList.size() == 0 ? View.VISIBLE : View.GONE); 127 | } 128 | }); 129 | } 130 | //点击条目 131 | private AdapterView.OnItemClickListener onWxListItemClickListener = new AdapterView.OnItemClickListener() { 132 | @Override 133 | public void onItemClick(AdapterView parent, View view, int position, long id) { 134 | Intent intent = new Intent(MainActivity.this, ContactsActivity.class); 135 | Map data = infoList.get(position); 136 | intent.putExtra("nickname", (String)data.get("nickname")); 137 | intent.putExtra("wxId", (String)data.get("wxId")); 138 | startActivity(intent); 139 | } 140 | }; 141 | 142 | } -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/ReplyActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule; 2 | 3 | import android.app.Service; 4 | import android.content.ComponentName; 5 | import android.content.Intent; 6 | import android.content.ServiceConnection; 7 | import android.database.sqlite.SQLiteDatabase; 8 | import android.os.Bundle; 9 | import android.os.IBinder; 10 | import android.view.Menu; 11 | import android.view.MenuItem; 12 | import android.widget.ListView; 13 | 14 | import androidx.annotation.NonNull; 15 | import androidx.annotation.Nullable; 16 | import androidx.appcompat.app.AppCompatActivity; 17 | 18 | import easynet.network.Session; 19 | 20 | public class ReplyActivity extends AppCompatActivity { 21 | 22 | private String wxId; 23 | private String nickname; 24 | private String contactWxId; 25 | private String contactNickname; 26 | private String contactConRemark; 27 | private ListView replyListView; 28 | private ReplyListAdapter listAdapter; 29 | private MService mService; 30 | private Session session; 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | setContentView(R.layout.activity_reply); 36 | setTitle("自动回复"); 37 | Intent intent = getIntent(); 38 | wxId = intent.getStringExtra("wxId"); 39 | nickname = intent.getStringExtra("nickname"); 40 | contactWxId = intent.getStringExtra("contactWxId"); 41 | contactNickname = intent.getStringExtra("contactNickname"); 42 | contactConRemark = intent.getStringExtra("contactConRemark"); 43 | replyListView = findViewById(R.id.replyList); 44 | Intent serviceIntent = new Intent(this, MService.class); 45 | bindService(serviceIntent, conn, Service.BIND_AUTO_CREATE); 46 | } 47 | @Override 48 | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 49 | super.onActivityResult(requestCode, resultCode, data); 50 | if (requestCode != 1 || requestCode != 1) { 51 | return; 52 | } 53 | listAdapter.refreshData(); 54 | } 55 | @Override 56 | public boolean onCreateOptionsMenu(Menu menu) { 57 | getMenuInflater().inflate(R.menu.activity_reply_msg, menu); 58 | return super.onCreateOptionsMenu(menu); 59 | } 60 | @Override 61 | public boolean onOptionsItemSelected(@NonNull MenuItem item) { 62 | if (item.getItemId() == R.id.add) { 63 | Intent intent = new Intent(ReplyActivity.this, ReplySettingsActivity.class); 64 | intent.putExtra("wxId", wxId); 65 | intent.putExtra("contactWxId", contactWxId); 66 | startActivityForResult(intent, 1); 67 | } 68 | return super.onOptionsItemSelected(item); 69 | } 70 | @Override 71 | protected void onDestroy() { 72 | unbindService(conn); 73 | mService = null; 74 | session.unregisterErrorCallback(errorCallback); 75 | super.onDestroy(); 76 | } 77 | //绑定服务回调 78 | private ServiceConnection conn = new ServiceConnection() { 79 | @Override 80 | public void onServiceConnected(ComponentName name, IBinder service) { 81 | MService.LocalBinder binder = (MService.LocalBinder)service; 82 | mService = binder.getService(); 83 | SQLiteDatabase db = mService.getSQLiteHelper().getWritableDatabase(); 84 | session = mService.getWxSession(wxId); 85 | session.registerErrorCallback(errorCallback); 86 | listAdapter = new ReplyListAdapter(ReplyActivity.this, wxId, contactWxId, db, session); 87 | replyListView.setAdapter(listAdapter); 88 | } 89 | 90 | @Override 91 | public void onServiceDisconnected(ComponentName name) { 92 | mService = null; 93 | } 94 | }; 95 | //session异常回调 96 | private Session.ErrorCallback errorCallback = new Session.ErrorCallback() { 97 | @Override 98 | public void run(Session session, Exception e) { 99 | runOnUiThread(new Runnable() { 100 | @Override 101 | public void run() { 102 | finish(); 103 | } 104 | }); 105 | } 106 | }; 107 | } -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/RoundImageView.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapShader; 6 | import android.graphics.Canvas; 7 | import android.graphics.Matrix; 8 | import android.graphics.Paint; 9 | import android.graphics.RectF; 10 | import android.graphics.Shader; 11 | import android.graphics.drawable.BitmapDrawable; 12 | import android.graphics.drawable.Drawable; 13 | import android.util.AttributeSet; 14 | 15 | import androidx.annotation.Nullable; 16 | 17 | public class RoundImageView extends androidx.appcompat.widget.AppCompatImageView { 18 | 19 | private int mBorderRadius = 10; 20 | 21 | private Paint mPaint; 22 | 23 | private Matrix mMatrix; 24 | 25 | private BitmapShader mBitmapShader; 26 | 27 | public RoundImageView(Context context) { 28 | this(context, null); 29 | } 30 | 31 | public RoundImageView(Context context, @Nullable AttributeSet attrs) { 32 | this(context, attrs, 0); 33 | } 34 | 35 | public RoundImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 36 | super(context, attrs, defStyleAttr); 37 | 38 | mMatrix = new Matrix(); 39 | mPaint = new Paint(); 40 | mPaint.setAntiAlias(true); 41 | } 42 | 43 | @Override 44 | protected void onDraw(Canvas canvas) { 45 | if (getDrawable() == null){ 46 | return; 47 | } 48 | Bitmap bitmap = drawableToBitmap(getDrawable()); 49 | mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); 50 | float scale = 1.0f; 51 | if (!(bitmap.getWidth() == getWidth() && bitmap.getHeight() == getHeight())) 52 | { 53 | // 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值; 54 | scale = Math.max(getWidth() * 1.0f / bitmap.getWidth(), 55 | getHeight() * 1.0f / bitmap.getHeight()); 56 | } 57 | // shader的变换矩阵,我们这里主要用于放大或者缩小 58 | mMatrix.setScale(scale, scale); 59 | // 设置变换矩阵 60 | mBitmapShader.setLocalMatrix(mMatrix); 61 | // 设置shader 62 | mPaint.setShader(mBitmapShader); 63 | canvas.drawRoundRect(new RectF(0,0,getWidth(),getHeight()), mBorderRadius, mBorderRadius, 64 | mPaint); 65 | } 66 | 67 | 68 | private Bitmap drawableToBitmap(Drawable drawable) 69 | { 70 | if (drawable instanceof BitmapDrawable) 71 | { 72 | BitmapDrawable bd = (BitmapDrawable) drawable; 73 | return bd.getBitmap(); 74 | } 75 | // 当设置不为图片,为颜色时,获取的drawable宽高会有问题,所有当为颜色时候获取控件的宽高 76 | int w = drawable.getIntrinsicWidth() <= 0 ? getWidth() : drawable.getIntrinsicWidth(); 77 | int h = drawable.getIntrinsicHeight() <= 0 ? getHeight() : drawable.getIntrinsicHeight(); 78 | Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); 79 | Canvas canvas = new Canvas(bitmap); 80 | drawable.setBounds(0, 0, w, h); 81 | drawable.draw(canvas); 82 | return bitmap; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/SendActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule; 2 | 3 | import android.app.Service; 4 | import android.content.ComponentName; 5 | import android.content.Intent; 6 | import android.content.ServiceConnection; 7 | import android.database.sqlite.SQLiteDatabase; 8 | import android.os.Bundle; 9 | import android.os.IBinder; 10 | import android.view.Menu; 11 | import android.view.MenuItem; 12 | import android.widget.ListView; 13 | 14 | import androidx.annotation.NonNull; 15 | import androidx.annotation.Nullable; 16 | import androidx.appcompat.app.AppCompatActivity; 17 | 18 | import com.example.fmodule.eventsystem.EventHandler; 19 | import com.example.fmodule.eventsystem.EventIdType; 20 | import com.example.fmodule.eventsystem.EventSystem; 21 | 22 | public class SendActivity extends AppCompatActivity { 23 | 24 | private String wxId; 25 | private String nickname; 26 | private String contactWxId; 27 | private String contactNickname; 28 | private String contactConRemark; 29 | private ListView listView; 30 | private SendListAdapter listAdapter; 31 | private MService mService; 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | setContentView(R.layout.activity_send); 36 | setTitle("自动发送"); 37 | Intent intent = getIntent(); 38 | wxId = intent.getStringExtra("wxId"); 39 | nickname = intent.getStringExtra("nickname"); 40 | contactWxId = intent.getStringExtra("contactWxId"); 41 | contactNickname = intent.getStringExtra("contactNickname"); 42 | contactConRemark = intent.getStringExtra("contactConRemark"); 43 | listView = findViewById(R.id.sendList); 44 | Intent serviceIntent = new Intent(this, MService.class); 45 | bindService(serviceIntent, conn, Service.BIND_AUTO_CREATE); 46 | 47 | } 48 | @Override 49 | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 50 | super.onActivityResult(requestCode, resultCode, data); 51 | if (requestCode != 1 || requestCode != 1) { 52 | return; 53 | } 54 | listAdapter.refreshData(); 55 | } 56 | @Override 57 | public boolean onCreateOptionsMenu(Menu menu) { 58 | getMenuInflater().inflate(R.menu.activity_send, menu); 59 | return super.onCreateOptionsMenu(menu); 60 | } 61 | @Override 62 | public boolean onOptionsItemSelected(@NonNull MenuItem item) { 63 | if (item.getItemId() == R.id.add) { 64 | Intent intent = new Intent(SendActivity.this, SendSettingsActivity.class); 65 | intent.putExtra("wxId", wxId); 66 | intent.putExtra("nickname", nickname); 67 | intent.putExtra("contactWxId", contactWxId); 68 | startActivityForResult(intent, 1); 69 | } 70 | return super.onOptionsItemSelected(item); 71 | } 72 | @Override 73 | protected void onDestroy() { 74 | EventSystem.getInstance().unregister(this); 75 | unbindService(conn); 76 | mService = null; 77 | super.onDestroy(); 78 | } 79 | @EventHandler(EventIdType.sendTaskCompleted) 80 | public void sendTaskCompletedHandler(int sendTaskId) { 81 | runOnUiThread(new Runnable() { 82 | @Override 83 | public void run() { 84 | listAdapter.refreshData(); 85 | } 86 | }); 87 | } 88 | //绑定服务回调 89 | private ServiceConnection conn = new ServiceConnection() { 90 | @Override 91 | public void onServiceConnected(ComponentName name, IBinder service) { 92 | MService.LocalBinder binder = (MService.LocalBinder)service; 93 | mService = binder.getService(); 94 | SQLiteDatabase db = mService.getSQLiteHelper().getWritableDatabase(); 95 | listAdapter = new SendListAdapter(SendActivity.this, wxId, contactWxId, mService, db); 96 | listView.setAdapter(listAdapter); 97 | EventSystem.getInstance().register(SendActivity.this); 98 | } 99 | 100 | @Override 101 | public void onServiceDisconnected(ComponentName name) { 102 | mService = null; 103 | } 104 | }; 105 | } -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/WXListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.util.Base64; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.BaseAdapter; 11 | import android.widget.ImageView; 12 | import android.widget.TextView; 13 | 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | public class WXListAdapter extends BaseAdapter { 18 | 19 | private List> list; 20 | private LayoutInflater inflater; 21 | private Context context; 22 | 23 | public WXListAdapter(Context context, List> list) { 24 | this.context = context; 25 | inflater = LayoutInflater.from(context); 26 | this.list = list; 27 | } 28 | @Override 29 | public int getCount() { 30 | return list.size(); 31 | } 32 | 33 | @Override 34 | public Object getItem(int position) { 35 | return list.get(position); 36 | } 37 | 38 | @Override 39 | public long getItemId(int position) { 40 | return position; 41 | } 42 | 43 | @Override 44 | public View getView(int position, View convertView, ViewGroup parent) { 45 | ViewHolder holder = null; 46 | 47 | if (convertView == null) { 48 | convertView = inflater.inflate(R.layout.activity_main_wxlist_item, null); 49 | holder = new ViewHolder(); 50 | holder.avatar = convertView.findViewById(R.id.avatarImage); 51 | holder.nickname = convertView.findViewById(R.id.nicknameText); 52 | holder.wxId = convertView.findViewById(R.id.wxIdText); 53 | convertView.setTag(holder); 54 | } else { 55 | holder = (ViewHolder) convertView.getTag(); 56 | } 57 | Map map = list.get(position); 58 | byte[] avatarBytes = Base64.decode((String)map.get("avatarBase64"), Base64.DEFAULT); 59 | Bitmap bitmap = BitmapFactory.decodeByteArray(avatarBytes, 0, avatarBytes.length); 60 | //Drawable avatarDrawable = new BitmapDrawable(context.getResources(), bitmap); 61 | holder.avatar.setImageBitmap(bitmap); 62 | holder.nickname.setText((String)map.get("nickname")); 63 | holder.wxId.setText((String)map.get("wxId")); 64 | 65 | return convertView; 66 | } 67 | 68 | private class ViewHolder { 69 | public ImageView avatar; 70 | public TextView nickname; 71 | public TextView wxId; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/eventsystem/EventHandler.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.eventsystem; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | @Documented 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Target({ElementType.METHOD}) 12 | public @interface EventHandler { 13 | String value(); 14 | } 15 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/eventsystem/EventHandlerProxy.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.eventsystem; 2 | 3 | import java.lang.reflect.Method; 4 | 5 | public class EventHandlerProxy { 6 | public Object object; 7 | public Method handler; 8 | 9 | public void invoke(Object... args) throws Exception { 10 | if (handler.getParameterTypes().length != args.length) { 11 | return; 12 | } 13 | if (handler != null) { 14 | handler.invoke(object, args); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/eventsystem/EventIdType.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.eventsystem; 2 | 3 | public class EventIdType { 4 | public static final String mServiceSessionAdded = "mServiceSessionAdded"; 5 | public static final String mServiceSessionRemoved = "mServiceSessionRemoved"; 6 | public static final String mServiceSessionChanged = "mServiceSessionChanged"; 7 | public static final String sendTaskCompleted = "sendTaskCompleted"; 8 | } 9 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/eventsystem/EventSystem.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.eventsystem; 2 | 3 | import android.app.Activity; 4 | import android.util.Log; 5 | 6 | import java.lang.reflect.Method; 7 | import java.util.ArrayList; 8 | import java.util.HashMap; 9 | import java.util.Iterator; 10 | import java.util.LinkedList; 11 | 12 | public class EventSystem { 13 | private final String TAG = "EventSystem"; 14 | private static EventSystem instance; 15 | private LinkedList handlerCache = new LinkedList<>(); 16 | private HashMap> handlers = new HashMap<>(); 17 | private HashMap> objHandlers = new HashMap<>(); 18 | 19 | private EventSystem() { 20 | 21 | } 22 | 23 | public static EventSystem getInstance() { 24 | if (instance == null) { 25 | instance = new EventSystem(); 26 | } 27 | return instance; 28 | } 29 | 30 | public void register(Activity object) { 31 | HashMap objHandlersMap = objHandlers.get(object); 32 | Class clazz = object.getClass(); 33 | Method[] methods = clazz.getMethods(); 34 | for (int i=0; i handlerList = handlers.get(eventId); 46 | if (handlerList == null) { 47 | handlerList = new ArrayList<>(); 48 | handlers.put(eventId, handlerList); 49 | } 50 | if (objHandlersMap == null) { 51 | objHandlersMap = new HashMap<>(); 52 | objHandlers.put(object, objHandlersMap); 53 | } 54 | if (objHandlersMap.containsKey(eventId)) { 55 | Log.e(TAG, "register: " + "注册事件失败!" + clazz.getName() + "." + method.getName() + " 事件ID已在该类中注册"); 56 | return; 57 | } 58 | //生成事件代理处理器 59 | EventHandlerProxy handler = handlerCache.poll(); 60 | if (handler == null) { 61 | handler = new EventHandlerProxy(); 62 | } 63 | handler.object = object; 64 | handler.handler = method; 65 | 66 | 67 | objHandlersMap.put(eventId, handler); 68 | handlerList.add(handler); 69 | } 70 | } 71 | 72 | public void unregister(Activity object) { 73 | HashMap objHs = objHandlers.get(object); 74 | if (objHs == null) { 75 | return; 76 | } 77 | for (String eventId : objHs.keySet()) { 78 | ArrayList list = handlers.get(eventId); 79 | if (list == null) { 80 | continue; 81 | } 82 | EventHandlerProxy proxy = objHs.get(eventId); 83 | list.remove(proxy); 84 | handlerCache.offer(proxy); 85 | } 86 | objHandlers.remove(object); 87 | } 88 | 89 | public void run(String eventId, Object... args) { 90 | ArrayList list = handlers.get(eventId); 91 | if (list == null) { 92 | return; 93 | } 94 | Iterator iterator = list.iterator(); 95 | while (iterator.hasNext()) { 96 | EventHandlerProxy proxy = iterator.next(); 97 | try { 98 | proxy.invoke(args); 99 | }catch (Exception e) { 100 | Log.e(TAG, "run: ", e); 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooks/AvatarGetter.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooks; 2 | 3 | import android.graphics.Bitmap; 4 | import android.util.Base64; 5 | 6 | import java.io.ByteArrayOutputStream; 7 | import java.lang.reflect.Constructor; 8 | import java.lang.reflect.Field; 9 | import java.lang.reflect.Method; 10 | 11 | import de.robv.android.xposed.XposedBridge; 12 | import de.robv.android.xposed.XposedHelpers; 13 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 14 | 15 | public class AvatarGetter { 16 | private Constructor cClassConstructor; 17 | private Method vmMethod; 18 | 19 | private Field wxVField; 20 | 21 | private Method ejMethod; 22 | 23 | public AvatarGetter(XC_LoadPackage.LoadPackageParam lParam) { 24 | try { 25 | String str1="V636F6D2E74656E63656E742E6D6D2E706C7567696E73646B2E75692E63F";//com.tencent.mm.pluginsdk.ui.c 26 | Class cClass = XposedHelpers.findClass(HookEntryUtil.decrypt(str1), lParam.classLoader); 27 | cClassConstructor = cClass.getConstructor(String.class, float.class); 28 | vmMethod = cClass.getMethod("vm", boolean.class); 29 | 30 | String str2="T636F6D2E74656E63656E742E6D6D2E706C7567696E73646B2E75692E6AD";//com.tencent.mm.pluginsdk.ui.j 31 | Class jClass = XposedHelpers.findClass(HookEntryUtil.decrypt(str2), lParam.classLoader); 32 | wxVField = jClass.getDeclaredField("wxV"); 33 | wxVField.setAccessible(true); 34 | 35 | String str3="I636F6D2E74656E63656E742E6D6D2E706C7567696E73646B2E75692E6A2461A";//com.tencent.mm.pluginsdk.ui.j$a 36 | Class j_aClass = XposedHelpers.findClass(HookEntryUtil.decrypt(str3), lParam.classLoader); 37 | //Kz是默认头像 38 | ejMethod = j_aClass.getMethod("ej", String.class); 39 | 40 | } catch (Exception e) { 41 | XposedBridge.log("initialize AvatarGetter error:" + e); 42 | } 43 | } 44 | 45 | public Bitmap getBitmap(String wxId) throws Exception { 46 | Object cObj = cClassConstructor.newInstance(wxId, 0.1f); 47 | vmMethod.invoke(cObj, false); 48 | Object wxVObj = wxVField.get(cObj); 49 | Bitmap bitmap = (Bitmap)ejMethod.invoke(wxVObj, wxId); 50 | return bitmap; 51 | } 52 | 53 | public byte[] getBytes(String wxId) throws Exception{ 54 | Bitmap bitmap = getBitmap(wxId); 55 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 56 | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream); 57 | byte[] bytes = stream.toByteArray(); 58 | return bytes; 59 | } 60 | 61 | public String getBase64(String wxId) throws Exception { 62 | byte[] bytes = getBytes(wxId); 63 | String avatarBase64 = Base64.encodeToString(bytes, Base64.DEFAULT); 64 | return avatarBase64; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooks/ContactsGetter.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooks; 2 | 3 | import android.database.Cursor; 4 | 5 | import java.lang.reflect.Method; 6 | import java.util.ArrayList; 7 | import java.util.LinkedList; 8 | import java.util.List; 9 | 10 | import de.robv.android.xposed.XposedBridge; 11 | import de.robv.android.xposed.XposedHelpers; 12 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 13 | 14 | public class ContactsGetter { 15 | private Method axAMethod; 16 | private Method aMethod; 17 | private String arg0; 18 | private String arg1; 19 | private List arg2; 20 | private LinkedList arg3; 21 | private boolean arg4; 22 | private boolean arg5; 23 | public ContactsGetter(XC_LoadPackage.LoadPackageParam lParam) { 24 | try { 25 | String str1 = "X636F6D2E74656E63656E742E6D6D2E6D6F64656C2E63R"; //com.tencent.mm.model.c 26 | Class cClass = XposedHelpers.findClass(HookEntryUtil.decrypt(str1), lParam.classLoader); 27 | axAMethod = cClass.getMethod("axA"); 28 | 29 | String str2 = "V636F6D2E74656E63656E742E6D6D2E73746F726167652E626CM"; //com.tencent.mm.storage.bl 30 | Class blClass = XposedHelpers.findClass(HookEntryUtil.decrypt(str2), lParam.classLoader); 31 | aMethod = blClass.getMethod("a", String.class, String.class, List.class, List.class, boolean.class, boolean.class); 32 | 33 | //String str3 = "K40616C6C2E636F6E746163742E776974686F75742E63686174726F6F6D2E776974686F75742E6F70656E696DV"; //@all.contact.without.chatroom.without.openim 34 | arg0 = "@all.contact"; 35 | arg1 = null; 36 | arg2 = new ArrayList<>(); 37 | arg2.add("tmessage"); 38 | arg2.add("officialaccounts"); 39 | arg2.add("helper_entry"); 40 | arg2.add("blogapp"); 41 | arg3 = new LinkedList<>(); 42 | arg3.offer("weixin"); 43 | arg4 = true; 44 | arg5 = true; 45 | }catch (Exception e) { 46 | XposedBridge.log("initialize ContactsGetter error: " + e); 47 | } 48 | } 49 | 50 | public Cursor get() throws Exception{ 51 | Object blObj = axAMethod.invoke(null); 52 | Object aObj = aMethod.invoke(blObj, arg0, arg1, arg2, arg3, arg4, arg5); 53 | Cursor cursor = (Cursor)aObj; 54 | return cursor; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooks/DecryptStrCallback.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooks; 2 | 3 | public class DecryptStrCallback { 4 | private int a; 5 | private int b; 6 | public DecryptStrCallback(int a, int b) { 7 | this.a = a; 8 | this.b = b; 9 | } 10 | 11 | public String run(String str) { 12 | int i = 2 - a; 13 | int j = 2 - b; 14 | return str.substring(i, str.length() - j); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooks/DiceHooker.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooks; 2 | 3 | import de.robv.android.xposed.XC_MethodHook; 4 | import de.robv.android.xposed.XposedBridge; 5 | import de.robv.android.xposed.XposedHelpers; 6 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 7 | 8 | public class DiceHooker { 9 | public DiceHooker(XC_LoadPackage.LoadPackageParam lParam, XC_MethodHook methodHook) { 10 | try { 11 | String str1 = "C636F6D2E74656E63656E742E6D6D2E73646B2E706C6174666F726D746F6F6C732E6273M"; //com.tencent.mm.sdk.platformtools.bs 12 | final Class clazz = XposedHelpers.findClass(HookEntryUtil.decrypt(str1), lParam.classLoader); 13 | XposedHelpers.findAndHookMethod(clazz,"jt", int.class, int.class, methodHook); 14 | }catch (Exception e) { 15 | XposedBridge.log("initialize DiceHooker error:" + e); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooks/HookEntryUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooks; 2 | 3 | import java.util.Random; 4 | 5 | public class HookEntryUtil { 6 | 7 | public static EncryptStrCallback encryptStrCallback; 8 | 9 | public static DecryptStrCallback decryptStrCallback; 10 | 11 | public interface EncryptStrCallback { 12 | String run(String str); 13 | } 14 | 15 | // public interface DecryptStrCallback { 16 | // String run(String str); 17 | // } 18 | 19 | public static String encrypt(String str) throws Exception{ 20 | if (encryptStrCallback == null) { 21 | return ""; 22 | } 23 | String hexStr = bytesToHexString(str.getBytes("ASCII")); 24 | return encryptStrCallback.run(hexStr); 25 | } 26 | 27 | public static String decrypt(String str) throws Exception{ 28 | if (decryptStrCallback == null) { 29 | return ""; 30 | } 31 | str = decryptStrCallback.run(str); 32 | byte[] bytes = hexStringToByte(str); 33 | String originalStr = new String(bytes, "ASCII"); 34 | return originalStr; 35 | } 36 | 37 | public static final String bytesToHexString(byte[] bArray) { 38 | StringBuffer sb = new StringBuffer(bArray.length); 39 | String sTemp; 40 | for (int i = 0; i < bArray.length; i++) { 41 | sTemp = Integer.toHexString(0xFF & bArray[i]); 42 | if (sTemp.length() < 2) 43 | sb.append(0); 44 | sb.append(sTemp.toUpperCase()); 45 | } 46 | return sb.toString(); 47 | } 48 | 49 | public static byte[] hexStringToByte(String hex) { 50 | int len = (hex.length() / 2); 51 | byte[] result = new byte[len]; 52 | char[] achar = hex.toCharArray(); 53 | for (int i = 0; i < len; i++) { 54 | int pos = i * 2; 55 | result[i] = (byte) (toByte(achar[pos]) << 4 | toByte(achar[pos + 1])); 56 | } 57 | return result; 58 | } 59 | 60 | private static byte toByte(char c) { 61 | byte b = (byte) "0123456789ABCDEF".indexOf(c); 62 | return b; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooks/MessageSender.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooks; 2 | 3 | import java.lang.reflect.Field; 4 | import java.lang.reflect.Method; 5 | 6 | import de.robv.android.xposed.XposedBridge; 7 | import de.robv.android.xposed.XposedHelpers; 8 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 9 | 10 | public class MessageSender { 11 | 12 | private Class eClass; 13 | private Class dClass; 14 | private Method aHDMethod; 15 | private Method executeMethod; 16 | //消息类型:普通消息、图片、语音 17 | private Object[] msgTypes; 18 | private Object msgObj; 19 | 20 | private Field content; 21 | private Field dnZ; 22 | private Field dpB; 23 | private Field dpg; 24 | private Field dqW; 25 | private Field dtX; 26 | private Field ghY; 27 | private Field gif; 28 | private Field hMj; 29 | private Field hMs; 30 | private Field hOn; 31 | private Field hOp; 32 | private Field hSL; 33 | private Field hSM; 34 | private Field hSN; 35 | private Field hSO; 36 | private Field hSP; 37 | private Field hSQ; 38 | private Field hSR; 39 | private Field hSS; 40 | private Field hST; 41 | private Field hSU; 42 | private Field hSV; 43 | private Field hSW; 44 | private Field hSX; 45 | private Field msgId; 46 | private Field thumbPath; 47 | private Field toUser; 48 | private Field type; 49 | 50 | public MessageSender(XC_LoadPackage.LoadPackageParam lParam) { 51 | try { 52 | String str1 = "K636F6D2E74656E63656E742E6D6D2E6D6F64656C6D756C74692E6F2465L"; //com.tencent.mm.modelmulti.o$e 53 | eClass = XposedHelpers.findClass(HookEntryUtil.decrypt(str1), lParam.classLoader); 54 | 55 | String str2 = "C636F6D2E74656E63656E742E6D6D2E6D6F64656C6D756C74692E6F2464H"; //com.tencent.mm.modelmulti.o$d 56 | dClass = XposedHelpers.findClass(HookEntryUtil.decrypt(str2), lParam.classLoader); 57 | aHDMethod = eClass.getMethod("aHD"); 58 | msgTypes = (Object[]) dClass.getDeclaredMethod("values").invoke(null); 59 | 60 | content = eClass.getField("content"); 61 | dnZ = eClass.getField("dnZ"); 62 | dpB = eClass.getField("dpB"); 63 | dpg = eClass.getField("dpg"); 64 | dqW = eClass.getField("dqW"); 65 | dtX = eClass.getField("dtX"); 66 | ghY = eClass.getField("ghY"); 67 | gif = eClass.getField("gif"); 68 | hMj = eClass.getField("hMj"); 69 | hMs = eClass.getField("hMs"); 70 | hOn = eClass.getField("hOn"); 71 | hOp = eClass.getField("hOp"); 72 | hSL = eClass.getField("hSL"); 73 | hSM = eClass.getField("hSM"); 74 | hSN = eClass.getField("hSN"); 75 | hSO = eClass.getField("hSO"); 76 | hSP = eClass.getField("hSP"); 77 | hSQ = eClass.getField("hSQ"); 78 | hSR = eClass.getField("hSR"); 79 | hSS = eClass.getField("hSS"); 80 | hST = eClass.getField("hST"); 81 | hSU = eClass.getField("hSU"); 82 | hSV = eClass.getField("hSV"); 83 | hSW = eClass.getField("hSW"); 84 | hSX = eClass.getField("hSX"); 85 | msgId = eClass.getField("msgId"); 86 | thumbPath = eClass.getField("thumbPath"); 87 | toUser = eClass.getField("toUser"); 88 | type = eClass.getField("type"); 89 | }catch (Exception e) { 90 | XposedBridge.log("initialize MessageSender error: " + e); 91 | } 92 | } 93 | 94 | private Object createMsgObj(String toUserId, String msg) throws Exception{ 95 | Object obj = eClass.newInstance(); 96 | content.set(obj, msg); 97 | dnZ.setInt(obj, 0); 98 | dpB.set(obj, null); 99 | dpg.setFloat(obj, 0.0f); 100 | dqW.setFloat(obj, 0.0f); 101 | dtX.setInt(obj, 0); 102 | ghY.setInt(obj, 0); 103 | gif.setBoolean(obj, false); 104 | hMj.setInt(obj, 0); 105 | hMs.set(obj, ""); 106 | hOn.set(obj, null); 107 | hOp.set(obj, ""); 108 | hSL.set(obj, null); 109 | hSM.setInt(obj, 5); 110 | hSN.set(obj, null); 111 | hSO.set(obj, null); 112 | hSP.setInt(obj, 0); 113 | hSQ.setBoolean(obj, false); 114 | hSR.setLong(obj, 0); 115 | hSS.setLong(obj, 0); 116 | hST.set(obj, null); 117 | hSU.setInt(obj, 0); 118 | hSV.setBoolean(obj, false); 119 | hSW.setInt(obj, 0); 120 | //消息类型:普通消息、图片、语音 121 | hSX.set(obj, msgTypes[0]); 122 | msgId.setLong(obj, 0); 123 | toUser.set(obj, toUserId); 124 | type.setInt(obj, 1); 125 | return obj; 126 | } 127 | 128 | public void send(String toUserId, String msg) throws Exception { 129 | if (toUserId == null || toUserId.isEmpty() || msg == null) { 130 | return; 131 | } 132 | if (msgObj == null) { 133 | msgObj = createMsgObj(toUserId, msg); 134 | } 135 | content.set(msgObj, msg); 136 | toUser.set(msgObj, toUserId); 137 | Object o1 = aHDMethod.invoke(msgObj); 138 | if (executeMethod == null) { 139 | executeMethod = o1.getClass().getMethod("execute"); 140 | } 141 | executeMethod.invoke(o1); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooks/PayeeQRCodeHook.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooks; 2 | 3 | import android.content.Context; 4 | import android.widget.Toast; 5 | 6 | import de.robv.android.xposed.IXposedHookLoadPackage; 7 | import de.robv.android.xposed.XC_MethodHook; 8 | import de.robv.android.xposed.XposedBridge; 9 | import de.robv.android.xposed.XposedHelpers; 10 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 11 | 12 | public class PayeeQRCodeHook implements IXposedHookLoadPackage { 13 | private XC_LoadPackage.LoadPackageParam lParam; 14 | private Context wxContext; 15 | @Override 16 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { 17 | if (!loadPackageParam.packageName.equals("com.tencent.mm")) { 18 | return; 19 | } 20 | lParam = loadPackageParam; 21 | hookContext(loadPackageParam); 22 | hook1(); 23 | } 24 | 25 | private void hookContext(XC_LoadPackage.LoadPackageParam lParam) { 26 | Class clazz = XposedHelpers.findClass("android.content.ContextWrapper", lParam.classLoader); 27 | 28 | XposedHelpers.findAndHookMethod( 29 | clazz, 30 | "getApplicationContext", 31 | new XC_MethodHook() { 32 | @Override 33 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 34 | super.afterHookedMethod(param); 35 | if (wxContext != null) { 36 | return; 37 | } 38 | wxContext = (Context) param.getResult(); 39 | } 40 | } 41 | ); 42 | } 43 | 44 | private void hook1() { 45 | Class clazz = XposedHelpers.findClass("com.tencent.mm.plugin.collect.model.b", lParam.classLoader); 46 | Class u_aClass = XposedHelpers.findClass("com.tencent.mm.platformtools.u$a", lParam.classLoader); 47 | XposedHelpers.findAndHookMethod(clazz, "a", Context.class, String.class, String.class, int.class, String.class, u_aClass, int.class, 48 | new XC_MethodHook() { 49 | @Override 50 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 51 | super.beforeHookedMethod(param); 52 | Toast.makeText(wxContext, "hook到了", Toast.LENGTH_SHORT).show(); 53 | XposedBridge.log("--------------------------------------"); 54 | XposedBridge.log("args1=" + param.args[1]); 55 | XposedBridge.log("args2=" + param.args[2]); 56 | XposedBridge.log("args3=" + param.args[3]); 57 | XposedBridge.log("args4=" + param.args[4]); 58 | XposedBridge.log("args6=" + param.args[6]); 59 | param.args[4] = "hello world"; 60 | } 61 | }); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooks/ReceiveMessageHook.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooks; 2 | 3 | import android.content.ContentValues; 4 | import android.content.Context; 5 | import android.widget.Toast; 6 | 7 | import java.util.Map; 8 | 9 | import de.robv.android.xposed.IXposedHookLoadPackage; 10 | import de.robv.android.xposed.XC_MethodHook; 11 | import de.robv.android.xposed.XposedBridge; 12 | import de.robv.android.xposed.XposedHelpers; 13 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 14 | 15 | public class ReceiveMessageHook implements IXposedHookLoadPackage { 16 | private Context wxContext; 17 | @Override 18 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { 19 | if (!loadPackageParam.packageName.equals("com.tencent.mm")) { 20 | return; 21 | } 22 | hookContext(loadPackageParam); 23 | hookMessage(loadPackageParam); 24 | } 25 | 26 | private void hookContext(XC_LoadPackage.LoadPackageParam lParam) { 27 | Class clazz = XposedHelpers.findClass("android.content.ContextWrapper", lParam.classLoader); 28 | 29 | XposedHelpers.findAndHookMethod( 30 | clazz, 31 | "getApplicationContext", 32 | new XC_MethodHook() { 33 | @Override 34 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 35 | super.afterHookedMethod(param); 36 | if (wxContext != null) { 37 | return; 38 | } 39 | wxContext = (Context) param.getResult(); 40 | } 41 | } 42 | ); 43 | } 44 | 45 | private void hookMessage(XC_LoadPackage.LoadPackageParam lParam) { 46 | Class clazz = XposedHelpers.findClass("com.tencent.wcdb.database.SQLiteDatabase", lParam.classLoader); 47 | XposedHelpers.findAndHookMethod( 48 | clazz, 49 | "insert", 50 | String.class, 51 | String.class, 52 | ContentValues.class, 53 | new XC_MethodHook() { 54 | @Override 55 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 56 | super.beforeHookedMethod(param); 57 | Toast.makeText(wxContext, "劫持到消息", Toast.LENGTH_SHORT).show(); 58 | XposedBridge.log("劫持成功: args0: " + param.args[0] + " args1: " + param.args[1]); 59 | String tableName = (String) param.args[0]; 60 | if (tableName.isEmpty() || !tableName.equals("message")) { 61 | return; 62 | } 63 | ContentValues contentValues = (ContentValues) param.args[2]; 64 | // Integer type = contentValues.getAsInteger("type"); 65 | // if (null == type || type != 1) { 66 | // return; 67 | // } 68 | 69 | XposedBridge.log("遍历content里的信息:"); 70 | for(Map.Entry item : contentValues.valueSet()) 71 | { 72 | XposedBridge.log(item.getKey() + " : " + item.getValue().toString()); 73 | } 74 | XposedBridge.log("遍历content里的信息完成\n\n\n"); 75 | } 76 | } 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooks/ReceiveMsgHooker.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooks; 2 | 3 | import android.content.ContentValues; 4 | 5 | import de.robv.android.xposed.XC_MethodHook; 6 | import de.robv.android.xposed.XposedBridge; 7 | import de.robv.android.xposed.XposedHelpers; 8 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 9 | 10 | public class ReceiveMsgHooker { 11 | public ReceiveMsgHooker(XC_LoadPackage.LoadPackageParam lParam, XC_MethodHook methodHook) { 12 | try { 13 | String str1 = "L636F6D2E74656E63656E742E776364622E64617461626173652E53514C6974654461746162617365E"; //com.tencent.wcdb.database.SQLiteDatabase 14 | Class clazz = XposedHelpers.findClass(HookEntryUtil.decrypt(str1), lParam.classLoader); 15 | XposedHelpers.findAndHookMethod( 16 | clazz, 17 | "insert", 18 | String.class, 19 | String.class, 20 | ContentValues.class, 21 | methodHook); 22 | }catch (Exception e) { 23 | XposedBridge.log("initialize ReceiveMsgHooker error:" + e); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooks/SendVoiceHook.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooks; 2 | 3 | import android.content.Context; 4 | import android.widget.Toast; 5 | 6 | import de.robv.android.xposed.IXposedHookLoadPackage; 7 | import de.robv.android.xposed.XC_MethodHook; 8 | import de.robv.android.xposed.XposedBridge; 9 | import de.robv.android.xposed.XposedHelpers; 10 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 11 | 12 | public class SendVoiceHook implements IXposedHookLoadPackage { 13 | private XC_LoadPackage.LoadPackageParam lParam; 14 | private Context wxContext; 15 | @Override 16 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { 17 | //XposedBridge.log(loadPackageParam.packageName); 18 | if (loadPackageParam.packageName.equals("com.tencent.mm")) { 19 | lParam = loadPackageParam; 20 | XposedBridge.log("开始劫持"); 21 | hookContext(loadPackageParam); 22 | hook2(); 23 | } 24 | } 25 | 26 | private void hookContext(XC_LoadPackage.LoadPackageParam lParam) { 27 | Class clazz = XposedHelpers.findClass("android.content.ContextWrapper", lParam.classLoader); 28 | 29 | XposedHelpers.findAndHookMethod( 30 | clazz, 31 | "getApplicationContext", 32 | new XC_MethodHook() { 33 | @Override 34 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 35 | super.afterHookedMethod(param); 36 | if (wxContext != null) { 37 | return; 38 | } 39 | wxContext = (Context) param.getResult(); 40 | } 41 | } 42 | ); 43 | } 44 | 45 | private void hook1() { 46 | Class clazz = XposedHelpers.findClass("com.tencent.mm.pluginsdk.ui.chat.ChatFooter", lParam.classLoader); 47 | XposedHelpers.findAndHookMethod(clazz, "eWo", new XC_MethodHook() { 48 | @Override 49 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 50 | super.beforeHookedMethod(param); 51 | Toast.makeText(wxContext, "调用了eWo", Toast.LENGTH_SHORT).show(); 52 | } 53 | }); 54 | } 55 | 56 | private void hook2() { 57 | Class clazz = XposedHelpers.findClass("com.tencent.mm.modelvoice.s", lParam.classLoader); 58 | XposedHelpers.findAndHookMethod(clazz, "at", String.class, int.class, new XC_MethodHook() { 59 | @Override 60 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 61 | super.beforeHookedMethod(param); 62 | Toast.makeText(wxContext, "调用了at", Toast.LENGTH_SHORT).show(); 63 | XposedBridge.log("arg0=" + param.args[0] + " arg1=" + param.args[1]); 64 | param.args[1] = 1000; 65 | } 66 | 67 | @Override 68 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 69 | super.afterHookedMethod(param); 70 | param.setResult(false); 71 | } 72 | }); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooks/UserInfoGetter.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooks; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.Map; 5 | 6 | import de.robv.android.xposed.XposedBridge; 7 | import de.robv.android.xposed.XposedHelpers; 8 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 9 | 10 | public class UserInfoGetter { 11 | private Method ayzMethod; 12 | private Method ayyMethod; 13 | private Method azpMethod; 14 | public UserInfoGetter(XC_LoadPackage.LoadPackageParam lParam) { 15 | try { 16 | String str1 = "C636F6D2E74656E63656E742E6D6D2E6D6F64656C2E75T"; //com.tencent.mm.model.u 17 | Class uClass = XposedHelpers.findClass(HookEntryUtil.decrypt(str1), lParam.classLoader); 18 | ayzMethod = uClass.getMethod("ayz"); 19 | ayyMethod = uClass.getMethod("ayy"); 20 | azpMethod = uClass.getMethod("azp"); 21 | }catch (Exception e) { 22 | XposedBridge.log("initialize UserInfoGetter error: " + e); 23 | } 24 | } 25 | 26 | public String getWxId() throws Exception{ 27 | return (String)ayzMethod.invoke(null); 28 | } 29 | 30 | public String getNickname() throws Exception{ 31 | return (String)ayyMethod.invoke(null); 32 | } 33 | 34 | public Map getLastInfo() throws Exception{ 35 | return (Map)azpMethod.invoke(null); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooks/VoiceHooker.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooks; 2 | 3 | import de.robv.android.xposed.XC_MethodHook; 4 | import de.robv.android.xposed.XposedBridge; 5 | import de.robv.android.xposed.XposedHelpers; 6 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 7 | 8 | public class VoiceHooker { 9 | public VoiceHooker(XC_LoadPackage.LoadPackageParam lParam, XC_MethodHook methodHook) { 10 | try { 11 | String str1 = "Q636F6D2E74656E63656E742E6D6D2E6D6F64656C766F6963652E73N"; //com.tencent.mm.modelvoice.s 12 | Class clazz = XposedHelpers.findClass(HookEntryUtil.decrypt(str1), lParam.classLoader); 13 | XposedHelpers.findAndHookMethod(clazz, "at", String.class, int.class, methodHook); 14 | }catch (Exception e) { 15 | XposedBridge.log("initialize VoiceHooker error:" + e); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooks/WXMessageUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooks; 2 | 3 | public class WXMessageUtil { 4 | public static final int[] types = new int[] { 5 | 1, //文本消息 6 | 3, //图片消息 7 | 34, //语音消息 8 | 37, //好友确认消息 9 | 40, //POSSIBLEFRIEND_MSG 10 | 42, //共享名片 11 | 43, //视频消息 12 | 47, //动画表情 13 | 48, //位置消息 14 | 49, //分享链接 15 | 50, //VOIPMSG 16 | 51, //微信初始化消息 17 | 52, //VOIPNOTIFY 18 | 53, //VOIPINVITE 19 | 62, //小视频 20 | 9999, //SYSNOTICE 21 | 10000, //系统消息 22 | 10002, //撤回消息 23 | 318767153, //收款信息 24 | }; 25 | 26 | public static boolean containsType(int type) { 27 | for (int i=0; i SendList; 12 | } 13 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooks/models/TalkModel.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooks.models; 2 | //定时发送消息请求远程接口时的携带参数 3 | public class TalkModel { 4 | //发送对象的微信号 5 | public String Talker; 6 | } 7 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooktask/ContactReplyTask.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooktask; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class ContactReplyTask { 6 | public String wxId; 7 | public ArrayList replyTasks; 8 | 9 | public ContactReplyTask() { 10 | wxId = ""; 11 | replyTasks = new ArrayList<>(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooktask/ContactSendTask.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooktask; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class ContactSendTask { 6 | public String wxId; 7 | public ArrayList sendTasks; 8 | 9 | public ContactSendTask() { 10 | wxId = ""; 11 | sendTasks = new ArrayList<>(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooktask/DiceTask.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooktask; 2 | 3 | import android.database.Cursor; 4 | 5 | public class DiceTask { 6 | public String wxId; 7 | public boolean isOn; 8 | public int number; 9 | 10 | public DiceTask() { 11 | isOn = false; 12 | number = 1; 13 | } 14 | 15 | public void convertFrom(Cursor cursor) { 16 | wxId = cursor.getString(0); 17 | isOn = cursor.getInt(1) == 1; 18 | number = cursor.getInt(2); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooktask/HookTask.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooktask; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | 6 | public class HookTask { 7 | public String wxId; 8 | public String nickname; 9 | public DiceTask diceTask; 10 | public HashMap contactsReplyTask; 11 | public HashMap contactsSendTask; 12 | public ArrayList globalReplyTasks; 13 | 14 | public HookTask() { 15 | wxId = ""; 16 | nickname = ""; 17 | diceTask = new DiceTask(); 18 | contactsReplyTask = new HashMap<>(); 19 | contactsSendTask = new HashMap<>(); 20 | globalReplyTasks = new ArrayList<>(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooktask/HookTaskHelper.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooktask; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | 6 | import com.alibaba.fastjson.JSON; 7 | import com.example.fmodule.MainActivity; 8 | import com.example.fmodule.eventsystem.EventIdType; 9 | import com.example.fmodule.eventsystem.EventSystem; 10 | import com.example.fmodule.message.hooktask.MSendTask; 11 | import com.example.fmodule.other.NetHelper; 12 | import com.example.fmodule.sqlite.SQLiteHelper; 13 | 14 | import java.io.FileInputStream; 15 | import java.io.FileOutputStream; 16 | import java.util.Calendar; 17 | import java.util.HashMap; 18 | import java.util.Timer; 19 | import java.util.TimerTask; 20 | 21 | import easynet.network.CircularBuffer; 22 | 23 | import static android.content.Context.MODE_PRIVATE; 24 | 25 | public class HookTaskHelper { 26 | public static void saveHookTask(Context context, HookTask hookTask) { 27 | try { 28 | String dataStr = JSON.toJSONString(hookTask); 29 | byte[] bytes = dataStr.getBytes("UTF-8"); 30 | FileOutputStream stream = context.openFileOutput(hookTask.wxId + ".txt", MODE_PRIVATE); 31 | stream.write(bytes); 32 | stream.flush(); 33 | }catch (Exception e) { 34 | e.printStackTrace(); 35 | } 36 | } 37 | 38 | private static CircularBuffer readBuffer; 39 | public static HookTask getHookTask(Context context, String wxId) { 40 | if (readBuffer == null) { 41 | readBuffer = new CircularBuffer(); 42 | } 43 | readBuffer.flush(); 44 | try { 45 | FileInputStream stream = context.openFileInput(wxId + ".txt"); 46 | byte[] bytes = new byte[1024]; 47 | while(true) { 48 | byte[] buffer = readBuffer.last(); 49 | int offset = readBuffer.getLastIndex(); 50 | int count = readBuffer.getChunkSize() - readBuffer.getLastIndex(); 51 | int len = stream.read(buffer, offset, count); 52 | if (len == -1) { 53 | break; 54 | }else { 55 | readBuffer.setLastIndexByOffset(len); 56 | } 57 | } 58 | byte[] data = readBuffer.toByteArray(); 59 | String dataStr = new String(data, "UTF-8"); 60 | HookTask hookTask = JSON.parseObject(dataStr, HookTask.class); 61 | return hookTask; 62 | }catch (Exception e) { 63 | e.printStackTrace(); 64 | HookTask ht = new HookTask(); 65 | ht.wxId = wxId; 66 | return ht; 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooktask/ReplyTask.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooktask; 2 | 3 | import android.database.Cursor; 4 | 5 | public class ReplyTask { 6 | public int id; 7 | public String wxId; 8 | public String fromUser; 9 | public boolean isOn; 10 | public enum PatternMode { fuzzy, precise, all } 11 | public PatternMode patternMode; 12 | public String patternMsg; 13 | public enum ReplyMode { fixedText, emojiUrl, serverApi } 14 | public ReplyMode replyMode; 15 | public String replyMsg; 16 | public String emojiUrl; 17 | public String serverApi; 18 | 19 | public ReplyTask() { 20 | id = -1; 21 | isOn = false; 22 | patternMode = PatternMode.fuzzy; 23 | replyMode = ReplyMode.fixedText; 24 | } 25 | 26 | public void convertFrom(Cursor cursor) { 27 | id = cursor.getInt(0); 28 | wxId = cursor.getString(1); 29 | fromUser = cursor.getString(2); 30 | isOn = cursor.getInt(3) == 1; 31 | patternMode = PatternMode.valueOf(cursor.getString(4)); 32 | patternMsg = cursor.getString(5); 33 | replyMode = ReplyMode.valueOf(cursor.getString(6)); 34 | replyMsg = cursor.getString(7); 35 | emojiUrl = cursor.getString(8); 36 | serverApi = cursor.getString(9); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooktask/SendTask.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooktask; 2 | 3 | import android.database.Cursor; 4 | 5 | import java.util.Date; 6 | 7 | public class SendTask { 8 | public int id; 9 | public String wxId; 10 | public String toUser; 11 | public String toUsers; 12 | public enum SendMode { fixedText, emojiUrl, serverApi } 13 | public SendMode sendMode; 14 | public String sendMsg; 15 | public String emojiUrl; 16 | public String serverApi; 17 | public enum SendTimeMode { timeout_5s, timeout_30s, timeout_1m, custom } 18 | public SendTimeMode sendTimeMode; 19 | public Date sendDate; 20 | 21 | public SendTask() { 22 | id = -1; 23 | sendMode = SendMode.fixedText; 24 | sendTimeMode = SendTimeMode.timeout_5s; 25 | } 26 | 27 | public void convertFrom(Cursor cursor) { 28 | id = cursor.getInt(0); 29 | wxId = cursor.getString(1); 30 | toUser = cursor.getString(2); 31 | toUsers = cursor.getString(3); 32 | sendMode = SendMode.valueOf(cursor.getString(4)); 33 | sendMsg = cursor.getString(5); 34 | emojiUrl = cursor.getString(6); 35 | serverApi = cursor.getString(7); 36 | sendTimeMode = SendTimeMode.valueOf(cursor.getString(8)); 37 | sendDate = new Date(); 38 | sendDate.setTime(cursor.getLong(9)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/hooktask/VoiceLenTask.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.hooktask; 2 | 3 | import android.database.Cursor; 4 | 5 | public class VoiceLenTask { 6 | public String wxId; 7 | public boolean isOn; 8 | public int number; 9 | 10 | public VoiceLenTask() { 11 | isOn = false; 12 | number = 1; 13 | } 14 | 15 | public void convertFrom(Cursor cursor) { 16 | wxId = cursor.getString(0); 17 | isOn = cursor.getInt(1) == 1; 18 | number = cursor.getInt(2); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/MessageManifest.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message; 2 | 3 | 4 | import com.example.fmodule.message.contacts.PContacts; 5 | import com.example.fmodule.message.contacts.QContacts; 6 | import com.example.fmodule.message.hooktask.MDiceTask; 7 | import com.example.fmodule.message.hooktask.MGlobalReplyTasks; 8 | import com.example.fmodule.message.hooktask.MHookTask; 9 | import com.example.fmodule.message.hooktask.MReplyTask; 10 | import com.example.fmodule.message.hooktask.MSendTask; 11 | import com.example.fmodule.message.hooktask.MVoiceLenTask; 12 | import com.example.fmodule.message.identify.PIdentify; 13 | import com.example.fmodule.message.identify.QIdentify; 14 | import com.example.fmodule.message.useravatar.PUserAvatar; 15 | import com.example.fmodule.message.useravatar.QUserAvatar; 16 | import com.example.fmodule.message.wxuserinfo.PWXUserInfo; 17 | import com.example.fmodule.message.wxuserinfo.QWXUserInfo; 18 | 19 | import easynet.network.MessageMgr; 20 | 21 | public class MessageManifest extends MessageMgr { 22 | 23 | public MessageManifest() { 24 | registerMessage((short)1, QIdentify.class); 25 | registerMessage((short)2, PIdentify.class); 26 | registerMessage((short)3, QWXUserInfo.class); 27 | registerMessage((short)4, PWXUserInfo.class); 28 | registerMessage((short)5, MHookTask.class); 29 | registerMessage((short)6, MDiceTask.class); 30 | registerMessage((short)7, QContacts.class); 31 | registerMessage((short)8, PContacts.class); 32 | registerMessage((short)9, MReplyTask.class); 33 | registerMessage((short)10, MGlobalReplyTasks.class); 34 | registerMessage((short)11, MSendTask.class); 35 | registerMessage((short)12, QUserAvatar.class); 36 | registerMessage((short)13, PUserAvatar.class); 37 | registerMessage((short)14, MVoiceLenTask.class); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/TestMessage.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message; 2 | 3 | import easynet.network.AMessage; 4 | 5 | public class TestMessage extends AMessage { 6 | public String message; 7 | } 8 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/contacts/PContacts.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message.contacts; 2 | 3 | import com.example.fmodule.other.ContactData; 4 | 5 | import java.util.ArrayList; 6 | 7 | import easynet.network.AResponse; 8 | 9 | public class PContacts extends AResponse { 10 | public ArrayList contacts; 11 | } 12 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/contacts/QContacts.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message.contacts; 2 | 3 | import easynet.network.ARequest; 4 | 5 | public class QContacts extends ARequest { 6 | } 7 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/hooktask/MDiceTask.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message.hooktask; 2 | 3 | import com.example.fmodule.hooktask.DiceTask; 4 | 5 | import easynet.network.AMessage; 6 | 7 | public class MDiceTask extends AMessage { 8 | public DiceTask diceTask; 9 | } 10 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/hooktask/MGlobalReplyTasks.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message.hooktask; 2 | 3 | import com.example.fmodule.hooktask.ReplyTask; 4 | 5 | import java.util.ArrayList; 6 | 7 | import easynet.network.AMessage; 8 | 9 | public class MGlobalReplyTasks extends AMessage { 10 | public ArrayList globalReplyTasks; 11 | } 12 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/hooktask/MHookTask.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message.hooktask; 2 | 3 | import com.example.fmodule.hooktask.HookTask; 4 | 5 | import easynet.network.AMessage; 6 | 7 | public class MHookTask extends AMessage { 8 | public HookTask hookTask; 9 | } 10 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/hooktask/MReplyTask.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message.hooktask; 2 | 3 | import com.example.fmodule.hooktask.ReplyTask; 4 | 5 | import easynet.network.AMessage; 6 | 7 | public class MReplyTask extends AMessage { 8 | public enum OpType {add, update, delete} 9 | public OpType opType; 10 | public ReplyTask replyTask; 11 | } 12 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/hooktask/MSendTask.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message.hooktask; 2 | 3 | import com.example.fmodule.hooktask.SendTask; 4 | 5 | import easynet.network.AMessage; 6 | 7 | public class MSendTask extends AMessage { 8 | public SendTask sendTask; 9 | } 10 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/hooktask/MVoiceLenTask.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message.hooktask; 2 | 3 | import com.example.fmodule.hooktask.VoiceLenTask; 4 | 5 | import easynet.network.AMessage; 6 | 7 | public class MVoiceLenTask extends AMessage { 8 | public VoiceLenTask task; 9 | } 10 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/identify/PIdentify.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message.identify; 2 | 3 | import easynet.network.AResponse; 4 | 5 | public class PIdentify extends AResponse { 6 | public boolean result; 7 | } 8 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/identify/QIdentify.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message.identify; 2 | 3 | import easynet.network.ARequest; 4 | 5 | public class QIdentify extends ARequest { 6 | public int identity; 7 | public static final int wx = 1; 8 | public static final int other = 2; 9 | public String id; 10 | public String nickname; 11 | public String avatarBase64; 12 | } 13 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/useravatar/PUserAvatar.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message.useravatar; 2 | 3 | import easynet.network.AResponse; 4 | 5 | public class PUserAvatar extends AResponse { 6 | public String wxId; 7 | public byte[] avatarData; 8 | } 9 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/useravatar/QUserAvatar.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message.useravatar; 2 | 3 | import easynet.network.ARequest; 4 | 5 | public class QUserAvatar extends ARequest { 6 | public String wxId; 7 | } 8 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/wxuserinfo/PWXUserInfo.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message.wxuserinfo; 2 | 3 | import easynet.network.AResponse; 4 | 5 | public class PWXUserInfo extends AResponse { 6 | public String nickname; 7 | } 8 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/message/wxuserinfo/QWXUserInfo.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.message.wxuserinfo; 2 | 3 | import easynet.network.ARequest; 4 | 5 | public class QWXUserInfo extends ARequest { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/other/ContactData.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.other; 2 | 3 | import android.database.Cursor; 4 | 5 | public class ContactData { 6 | 7 | public String username; 8 | public String nickname; 9 | public String alias; 10 | public String conRemark; 11 | 12 | public void convertFrom(Cursor cursor) { 13 | username = cursor.getString(0); 14 | nickname = cursor.getString(1); 15 | alias = cursor.getString(2); 16 | conRemark = cursor.getString(3); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/other/NetHelper.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.other; 2 | 3 | import java.util.Hashtable; 4 | 5 | import easynet.network.Network; 6 | import easynet.network.Session; 7 | 8 | public class NetHelper { 9 | public static Network network; 10 | private static Hashtable wxSessions; 11 | } 12 | -------------------------------------------------------------------------------- /Trunk/app/src/main/java/com/example/fmodule/sqlite/SQLiteHelper.java: -------------------------------------------------------------------------------- 1 | package com.example.fmodule.sqlite; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.database.sqlite.SQLiteOpenHelper; 6 | 7 | import androidx.annotation.Nullable; 8 | 9 | public class SQLiteHelper extends SQLiteOpenHelper { 10 | 11 | public static final String tableSendTask = "sendTask"; 12 | public static final String tableReplyTask = "replyTask"; 13 | public static final String tableDiceTask = "diceTask"; 14 | public static final String tableVoiceLenTask = "voiceLenTask"; 15 | 16 | private final String createSendTaskTable = "create table sendTask( " + 17 | "id integer primary key autoincrement, " + 18 | "wxId text not null, " + 19 | "toUser text not null, " + //all:发送给全部人 20 | "toUsers text ," + 21 | "sendMode varchar(20) not null," + //fixedText, serverApi 22 | "sendMsg text , " + 23 | "emojiUrl text , " + 24 | "serverApi text , " + 25 | "sendTimeMode varchar(20) not null, " + //timeout_5s, timeout_30s, timeout_1m, custom 26 | "sendDate integer not null" + 27 | ")"; 28 | 29 | private final String createReplyTaskTable = "create table replyTask(" + 30 | "id integer primary key autoincrement, " + 31 | "wxId text not null, " + 32 | "fromUser text not null, " + //all:匹配全部人 33 | "isOn integer not null, " + 34 | "patternMode varchar(20) not null, " + //fuzzy, precise, all 35 | "patternMsg text , " + 36 | "replyMode varchar(20) not null, " + // fixedText, serverApi 37 | "replyMsg text , " + 38 | "emojiUrl text , " + 39 | "serverApi text " + 40 | ")"; 41 | 42 | private final String createDiceTaskTable = "create table diceTask(" + 43 | "wxId text primary key, " + 44 | "isOn integer not null, " + 45 | "number integer not null" + 46 | ")"; 47 | 48 | private final String createVoiceLenTaskTable = "create table voiceLenTask(" + 49 | "wxId text primary key," + 50 | "isOn integer not null," + 51 | "number integer not null" + 52 | ")"; 53 | 54 | public SQLiteHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) { 55 | super(context, name, factory, version); 56 | } 57 | 58 | @Override 59 | public void onCreate(SQLiteDatabase db) { 60 | db.execSQL(createSendTaskTable); 61 | db.execSQL(createReplyTaskTable); 62 | db.execSQL(createDiceTaskTable); 63 | db.execSQL(createVoiceLenTaskTable); 64 | } 65 | 66 | @Override 67 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/anim/dialog_enter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/anim/dialog_exit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/anim/rotate_animation.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 13 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/activity_action_bar_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/activity_send_settings_select_contacts_btn_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/activity_send_settings_select_contacts_text_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/src/main/res/drawable/avatar.jpg -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/dialog_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | android:drawable="@drawable/loading_icon" 4 | android:fromDegrees="0" 5 | android:pivotX="50%" 6 | android:pivotY="50%" 7 | android:toDegrees="360" 8 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/ghost_white_bg_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/icon_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/src/main/res/drawable/icon_add.png -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/icon_add_primary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/src/main/res/drawable/icon_add_primary.png -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/icon_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/src/main/res/drawable/icon_delete.png -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/icon_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/src/main/res/drawable/icon_edit.png -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/icon_edit1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/src/main/res/drawable/icon_edit1.png -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/icon_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/src/main/res/drawable/icon_menu.png -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/icon_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/src/main/res/drawable/icon_search.png -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/loading_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/src/main/res/drawable/loading_bg.9.png -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/popup_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0lfh0/FModule/5a76f2bc82e23cb7b46500b99c4027fa659e5b33/Trunk/app/src/main/res/drawable/popup_bg.9.png -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/primary_bg_circle_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/search_bar_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/drawable/white_bg_circle_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 20 | 21 | 31 | 32 | 45 | 46 | 57 | 58 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/layout/activity_contacts.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/layout/activity_contacts_list_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | 20 | 33 | 34 | 46 | 47 | 54 | 55 | -------------------------------------------------------------------------------- /Trunk/app/src/main/res/layout/activity_contacts_profile.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 28 | 29 | 40 | 41 | 52 | 53 | 60 | 61 | 62 |