├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── hosea
│ │ │ └── messagerelayer
│ │ │ ├── App.java
│ │ │ ├── activity
│ │ │ ├── AboutActivity.java
│ │ │ ├── AccessibilityOpenHelperActivity.java
│ │ │ ├── BaseActivity.java
│ │ │ ├── ContactListActivity.java
│ │ │ ├── EmailRelayerActivity.java
│ │ │ ├── KeywordActivity.java
│ │ │ ├── MainActivity.java
│ │ │ ├── RuleActivity.java
│ │ │ ├── SelectedContactActivity.java
│ │ │ ├── SmsActivity.java
│ │ │ ├── SmsBlackActivity.java
│ │ │ ├── SmsRelayerActivity.java
│ │ │ ├── StartActivity.java
│ │ │ └── WeChatConfigurationAct.java
│ │ │ ├── adapter
│ │ │ ├── ContactListAdapter.java
│ │ │ ├── ContactSelectedAdapter.java
│ │ │ ├── SmsAdapter.java
│ │ │ ├── decoration
│ │ │ │ └── ContactDecoration.java
│ │ │ └── holder
│ │ │ │ └── ContactHolder.java
│ │ │ ├── bean
│ │ │ ├── Contact.java
│ │ │ ├── EmailMessage.java
│ │ │ └── SmsBean.java
│ │ │ ├── confing
│ │ │ ├── Constant.java
│ │ │ └── SMSConfig.java
│ │ │ ├── listener
│ │ │ └── ICustomCompletedListener.java
│ │ │ ├── receiver
│ │ │ ├── BatterReceiver.java
│ │ │ └── MessageReceiver.java
│ │ │ ├── service
│ │ │ ├── AccessibilitySampleService.java
│ │ │ ├── ForegroundService.java
│ │ │ └── SmsService.java
│ │ │ └── utils
│ │ │ ├── AccessibilityUtil.java
│ │ │ ├── ContactManager.java
│ │ │ ├── EmailRelayerManager.java
│ │ │ ├── FormatMobile.java
│ │ │ ├── NativeDataManager.java
│ │ │ ├── OpenAccessibilitySettingHelper.java
│ │ │ ├── SmsRelayerManager.java
│ │ │ ├── WeChatRelayerManager.java
│ │ │ ├── db
│ │ │ ├── DataBaseHelper.java
│ │ │ └── DataBaseManager.java
│ │ │ └── permission
│ │ │ ├── DefaultRationale.java
│ │ │ └── PermissionSetting.java
│ └── res
│ │ ├── anim
│ │ ├── accessibility_bottom_in.xml
│ │ └── accessibility_bottom_out.xml
│ │ ├── drawable
│ │ ├── bg_add_contact.xml
│ │ ├── bg_add_one.xml
│ │ ├── bg_keyword_four.xml
│ │ ├── bg_keyword_one.xml
│ │ ├── bg_keyword_three.xml
│ │ ├── bg_keyword_two.xml
│ │ ├── email_image.png
│ │ ├── icon_wechat.png
│ │ ├── setting_pro.png
│ │ └── sms_image.png
│ │ ├── layout
│ │ ├── activitiy_wechat.xml
│ │ ├── activity_about.xml
│ │ ├── activity_accessibility_transparent_layout.xml
│ │ ├── activity_contact_list.xml
│ │ ├── activity_email_relayer.xml
│ │ ├── activity_keyword.xml
│ │ ├── activity_main.xml
│ │ ├── activity_rule.xml
│ │ ├── activity_selected_contact.xml
│ │ ├── activity_sms_list.xml
│ │ ├── activity_sms_relayer.xml
│ │ ├── dialog_edit.xml
│ │ ├── dialog_email_account.xml
│ │ ├── dialog_email_servicer.xml
│ │ ├── dialog_progress.xml
│ │ ├── item_contact.xml
│ │ ├── item_keyword.xml
│ │ ├── item_selected_contact.xml
│ │ └── item_sms.xml
│ │ ├── mipmap-xhdpi
│ │ ├── ic_about.png
│ │ ├── ic_add.png
│ │ ├── ic_del.png
│ │ ├── ic_find.png
│ │ ├── ic_ok.png
│ │ ├── ic_remove.png
│ │ ├── ic_save.png
│ │ ├── ic_selected.png
│ │ ├── ic_send_off.png
│ │ ├── ic_send_on.png
│ │ ├── ic_skip.png
│ │ ├── ic_unselected.png
│ │ └── icon.png
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ │ └── xml
│ │ └── accessibility_config.xml
│ └── test
│ └── java
│ └── com
│ └── hosea
│ └── messagerelayer
│ └── ExampleUnitTestMain.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | /.idea/
10 | .idea/
11 | .externalNativeBuild
12 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | 1.8
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 致敬前辈
2 |
3 |
4 |
5 | [MessageRelayer](https://github.com/HaoFeiWang/MessageRelayer)基于他的源项目做了一些优化再次感谢这位作者的开源..
6 | 下面的使用方法基本一样.
7 |
8 | ## 我做了哪些优化?
9 |
10 | ### 1.新增内部转发内容到指定号码.
11 |
12 | 有一些验证需要原手机发短信使用。
13 |
14 | 在转发短信模块下,可以设置哪个手机号发过来的号码会被转发,一般设置为自己的手机号。
15 | 当上面设置的手机号发短信过来时,会根据规则转发这个短信到指定的号码。
16 | 设置分割规则,如'#'时,123#456,会触发转发,这个号码会发送信息456到123这个号码中。
17 |
18 | ### 2.优化邮件title
19 |
20 | 支持双卡,会根据收到短信卡的后4位来当做邮件的title,如果有验证码会直接直接显示在title中不需要打开邮件查看。
21 |
22 |
23 |
24 | ### 3.适配androidX,以及权限申请
25 |
26 | 1. 发短信和接收短信权限(转发短信和接收短信)。
27 | 2. 读取手机信息权限(获取收手机卡信息)。
28 | 3. 通知权限(为了保活)。
29 | 4. 自启动权限(开机自动启动)。
30 | 5. 省电策略改为无限制(为了保活)。
31 |
32 | ### 自动转发短信至目标邮箱
33 |
34 | 1. 开启转发至邮箱。
35 | 2. 设置SMTP服务器(QQ、163、126、Gmail、Outlook、自定义)。如果选择自定义SMTP服务器,则需要设置主机名和端口号。
36 | 推荐开启SSL方式,不开启可能会有未知错误,例如QQ邮箱。
37 | 3. 设置SMTP邮箱和密码(发送方账号,必须属于SMTP服务器的账号)。需要开启邮箱的SMTP服务,并且密码为其对应的授权码,详细操作参见各邮箱网站的帮助页面。
38 | 4. 设置目标邮箱账号(接收方账号,任意服务器均可)。
39 | 5. 设置发送方的名称(默认为:短信助手)。
40 | 6. 设置邮件主题(默认为:短信转发)。
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | gen/
13 | out/
14 |
15 | # Gradle files
16 | .gradle/
17 | gradle/
18 | build/
19 | gradlew*
20 | # gradle.properties
21 |
22 | # Local configuration file (sdk path, etc)
23 |
24 |
25 | # Proguard folder generated by Eclipse
26 | proguard/
27 |
28 | # Log Files
29 | *.log
30 |
31 | # Eclipse project files
32 | .classpath
33 | .project
34 | project.properties
35 | local.properties
36 | # IDEA project files
37 | *.iml
38 | .idea/
39 |
40 | # OS generated files
41 | .DS_Store
42 | .DS_Store?
43 | ._*
44 | .Spotlight-V100
45 | .Trashes
46 | ehthumbs.db
47 | Thumbs.db
48 | src.zip
49 |
50 | # others
51 | ant.properties
52 | build.xml
53 | map.txt
54 |
55 | # freeline
56 |
57 | freeline/
58 | freeline.py
59 | freeline_project_description.json
60 |
61 | VirtualAppDemo/
62 | VirtualLib/
63 |
64 | *.zip
65 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 |
4 | android {
5 | compileSdkVersion 29
6 | buildToolsVersion "29.0.2"
7 |
8 | defaultConfig {
9 | applicationId "com.hosea.messagerelayer"
10 | minSdkVersion 22
11 | targetSdkVersion 29
12 | versionCode 1
13 | versionName "1.0.1"
14 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
15 | }
16 |
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | compileOptions {
25 | sourceCompatibility JavaVersion.VERSION_1_8
26 | targetCompatibility JavaVersion.VERSION_1_8
27 | }
28 | }
29 |
30 | dependencies {
31 | implementation fileTree(include: ['*.jar'], dir: 'libs')
32 | androidTestCompile('androidx.test.espresso:espresso-core:3.1.0', {
33 | exclude group: 'com.android.support', module: 'support-annotations'
34 | })
35 | implementation 'androidx.appcompat:appcompat:1.0.0'
36 | testImplementation 'junit:junit:4.12'
37 | implementation 'com.sun.mail:android-mail:1.5.5'
38 | implementation 'com.sun.mail:android-activation:1.5.5'
39 | implementation 'com.squareup.okhttp3:okhttp:3.6.0'
40 | implementation 'androidx.recyclerview:recyclerview:1.0.0'
41 | implementation 'com.google.android.flexbox:flexbox:3.0.0'
42 | implementation 'com.yanzhenjie:permission:2.0.0-rc4'
43 | implementation 'com.blankj:utilcodex:1.31.1'
44 |
45 | // implementation 'com.xdandroid:hellodaemon:+'
46 | }
47 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
27 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
51 |
52 |
53 |
54 |
55 |
58 |
61 |
64 |
65 |
68 |
69 |
70 |
73 |
74 |
77 |
81 |
84 |
87 |
88 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
105 |
106 |
110 |
111 |
112 |
113 |
116 |
117 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/App.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer;
2 |
3 | import android.app.Application;
4 |
5 | import com.blankj.utilcode.util.CrashUtils;
6 | import com.blankj.utilcode.util.LogUtils;
7 |
8 | /**
9 | * Created by heliu on 2018/7/23.
10 | */
11 |
12 | public class App extends Application {
13 | @Override
14 | public void onCreate() {
15 | super.onCreate();
16 | //需要在 Application 的 onCreate() 中调用一次 DaemonEnv.initialize()
17 | CrashUtils.init(new CrashUtils.OnCrashListener() {
18 | @Override
19 | public void onCrash(CrashUtils.CrashInfo crashInfo) {
20 | LogUtils.e("----->onCrash: " + crashInfo);
21 | }
22 | });
23 |
24 |
25 | final LogUtils.Config config = LogUtils.getConfig()
26 | .setLogSwitch(true)// 设置 log 总开关,包括输出到控制台和文件,默认开
27 | .setConsoleSwitch(true)// 设置是否输出到控制台开关,默认开
28 | .setGlobalTag("")// 设置 log 全局标签,默认为空
29 | .setSaveDays(20)
30 | // 当全局标签不为空时,我们输出的 log 全部为该 tag,
31 | // 为空时,如果传入的 tag 为空那就显示类名,否则显示 tag
32 | .setLogHeadSwitch(true)// 设置 log 头信息开关,默认为开
33 | // testVersion 0000-0>BuildConfig.DEBUG
34 | .setLog2FileSwitch(true)// 打印 log 时是否存到文件的开关,默认关
35 | .setDir("")// 当自定义路径为空时,写入应用的/cache/log/目录中
36 | .setFilePrefix("sre")// 当文件前缀为空时,默认为"util",即写入文件为"util-MM-dd.txt"
37 | .setBorderSwitch(false)// 输出日志是否带边框开关,默认开
38 | .setConsoleFilter(LogUtils.V)// log 的控制台过滤器,和 logcat 过滤器同理,默认 Verbose
39 | .setFileFilter(LogUtils.V)// log 文件过滤器,和 logcat 过滤器同理,默认 Verbose
40 | .setStackDeep(1);// log 栈深度,默认为 1
41 | new Thread(new Runnable() {
42 | @Override
43 | public void run() {
44 | LogUtils.d(config.toString());
45 | }
46 | }).start();
47 |
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/activity/AboutActivity.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.activity;
2 |
3 |
4 | import android.os.Bundle;
5 | import android.view.MenuItem;
6 |
7 | import androidx.appcompat.app.ActionBar;
8 | import androidx.appcompat.app.AppCompatActivity;
9 | import androidx.recyclerview.widget.RecyclerView;
10 |
11 | import com.hosea.messagerelayer.R;
12 |
13 | public class AboutActivity extends AppCompatActivity {
14 |
15 | @Override
16 | protected void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | setContentView(R.layout.activity_about);
19 | initActionbar();
20 |
21 | }
22 |
23 | private void initActionbar(){
24 | ActionBar actionBar = getSupportActionBar();
25 | actionBar.setDisplayHomeAsUpEnabled(true);
26 | }
27 |
28 | @Override
29 | public boolean onOptionsItemSelected(MenuItem item) {
30 | if(item.getItemId() == android.R.id.home){
31 | finish();
32 | }
33 | return super.onOptionsItemSelected(item);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/activity/AccessibilityOpenHelperActivity.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.activity;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.os.Handler;
7 | import android.os.Looper;
8 | import android.widget.Toast;
9 |
10 | import com.hosea.messagerelayer.R;
11 | import com.hosea.messagerelayer.utils.AccessibilityUtil;
12 |
13 | import java.util.Timer;
14 | import java.util.TimerTask;
15 |
16 |
17 |
18 | /**
19 | * 辅助功能权限打开辅助activity,用于启动辅助功能设置页面
20 | */
21 | public class AccessibilityOpenHelperActivity extends Activity {
22 | private boolean isFirstCome = true;
23 | private static final String ACTION = "action";
24 | private static final String ACTION_FINISH_SELF = "action_finis_self";
25 |
26 | private Timer timer;
27 | private TimerTask timerTask;
28 | private int mTimeoutCounter = 0;
29 |
30 | private int TIMEOUT_MAX_INTERVAL = 60 * 2; // 2 min
31 |
32 | private long TIMER_CHECK_INTERVAL = 1000;
33 | protected static Handler mHandle = new Handler();
34 | protected static Runnable tipToastDelayRunnable;
35 |
36 | private static void removeDelayedToastTask() {
37 | if (mHandle != null && tipToastDelayRunnable != null) {
38 | mHandle.removeCallbacks(tipToastDelayRunnable);
39 | }
40 | }
41 |
42 | @Override
43 | protected void onCreate(Bundle savedInstanceState) {
44 | super.onCreate(savedInstanceState);
45 | setContentView(R.layout.activity_accessibility_transparent_layout);
46 | Intent intent = getIntent();
47 | if (intent != null && intent.getExtras() != null) {
48 | String action = intent.getStringExtra(ACTION);
49 | if (ACTION_FINISH_SELF.equals(action)) {
50 | finishCurrentActivity();
51 | return;
52 | }
53 | }
54 | mTimeoutCounter = 0;
55 | }
56 |
57 | @Override
58 | public void onBackPressed() {
59 | super.onBackPressed();
60 | finishCurrentActivity();
61 | }
62 |
63 | @Override
64 | protected void onNewIntent(Intent intent) {
65 | super.onNewIntent(intent);
66 | if (intent != null && intent.getExtras() != null) {
67 | String action = intent.getStringExtra(ACTION);
68 | if (ACTION_FINISH_SELF.equals(action)) {
69 | finishCurrentActivity();
70 | }
71 | }
72 | }
73 |
74 | @Override
75 | protected void onResume() {
76 | super.onResume();
77 | if (!isFirstCome) {
78 | removeDelayedToastTask();
79 | finishCurrentActivity();
80 | } else {
81 | jumpActivities();
82 | startCheckAccessibilityOpen();
83 | }
84 | isFirstCome = false;
85 | }
86 |
87 | private void jumpActivities() {
88 | try {
89 | Intent intent = AccessibilityUtil.getAccessibilitySettingPageIntent(this);
90 | startActivity(intent);
91 | } catch (Exception e) {
92 | e.printStackTrace();
93 | }
94 | }
95 |
96 | @Override
97 | protected void onDestroy() {
98 | freeTimeTask();
99 | super.onDestroy();
100 | }
101 |
102 | private void finishCurrentActivity() {
103 | freeTimeTask();
104 | finish();
105 | }
106 |
107 | private void startCheckAccessibilityOpen() {
108 | freeTimeTask();
109 | initTimeTask();
110 | timer.schedule(timerTask, 0, TIMER_CHECK_INTERVAL);
111 | }
112 |
113 | private void initTimeTask() {
114 | timer = new Timer();
115 | mTimeoutCounter = 0;
116 | timerTask = new TimerTask() {
117 |
118 | @SuppressWarnings("static-access")
119 | @Override
120 | public void run() {
121 | if (AccessibilityUtil.isAccessibilitySettingsOn(AccessibilityOpenHelperActivity.this)) {
122 | freeTimeTask();
123 | Looper.prepare();
124 | try {
125 | AccessibilityOpenHelperActivity.this.runOnUiThread(new Runnable() {
126 | @Override
127 | public void run() {
128 | Toast.makeText(AccessibilityOpenHelperActivity.this, "辅助功能开启成功", Toast.LENGTH_SHORT).show();
129 | }
130 | });
131 | Intent intent = new Intent();
132 | intent.putExtra(ACTION, ACTION_FINISH_SELF);
133 | intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
134 | intent.setClass(AccessibilityOpenHelperActivity.this, AccessibilityOpenHelperActivity.this.getClass());
135 | startActivity(intent);
136 | } catch (Exception e) {
137 | e.printStackTrace();
138 | }
139 | Looper.loop();
140 | }
141 |
142 | //超过2分钟超时,就释放timer。
143 | //解决: 可能有这样的一个场景,用户打开红包辅助功能,不勾选“猎豹安全大师”就退出到其他地方
144 | // 然后再另外一个场景又去勾选辅助功能,这个时候还是弹出红包场景,用户体验就很怪了
145 | mTimeoutCounter++;
146 | if (mTimeoutCounter > TIMEOUT_MAX_INTERVAL) {
147 | freeTimeTask();
148 | }
149 | }
150 | };
151 | }
152 |
153 | private void freeTimeTask() {
154 | if (timerTask != null) {
155 | timerTask.cancel();
156 | timerTask = null;
157 | }
158 | if (timer != null) {
159 | timer.cancel();
160 | timer = null;
161 | }
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/activity/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.activity;
2 |
3 | import android.os.Bundle;
4 |
5 | import android.widget.Toast;
6 |
7 | import androidx.annotation.NonNull;
8 | import androidx.annotation.Nullable;
9 | import androidx.appcompat.app.AppCompatActivity;
10 |
11 | import com.hosea.messagerelayer.listener.ICustomCompletedListener;
12 | import com.hosea.messagerelayer.utils.permission.DefaultRationale;
13 | import com.hosea.messagerelayer.utils.permission.PermissionSetting;
14 | import com.yanzhenjie.permission.Action;
15 | import com.yanzhenjie.permission.AndPermission;
16 | import com.yanzhenjie.permission.Rationale;
17 |
18 |
19 | import java.util.List;
20 |
21 | /**
22 | * Created by heliu on 2018/6/29.
23 | */
24 |
25 | public class BaseActivity extends AppCompatActivity {
26 | private Rationale mRationale;
27 | private PermissionSetting mSetting;
28 | @Override
29 | protected void onCreate(@Nullable Bundle savedInstanceState) {
30 | super.onCreate(savedInstanceState);
31 | mRationale = new DefaultRationale();
32 | mSetting = new PermissionSetting(this);
33 |
34 | }
35 |
36 |
37 | public void requestPermission(final ICustomCompletedListener listener, String... permissions) {
38 | AndPermission.with(this)
39 | .permission(permissions)
40 | .rationale(mRationale)
41 | .onGranted(new Action() {
42 | @Override
43 | public void onAction(List permissions) {
44 | // toast(R.string.successfully);
45 | if (listener != null) {
46 | listener.success();
47 | }
48 | }
49 | })
50 | .onDenied(new Action() {
51 | @Override
52 | public void onAction(@NonNull List permissions) {
53 | if (listener != null) {
54 | listener.failed(null);
55 | }
56 | Toast.makeText(BaseActivity.this, "失败", Toast.LENGTH_SHORT);
57 | if (AndPermission.hasAlwaysDeniedPermission(BaseActivity.this, permissions)) {
58 | mSetting.showSetting(permissions);
59 | }
60 | }
61 | })
62 | .start();
63 | }
64 |
65 | public void requestPermission(final ICustomCompletedListener listener, String[]... permissions) {
66 | AndPermission.with(this)
67 | .permission(permissions)
68 | .rationale(mRationale)
69 | .onGranted(new Action() {
70 | @Override
71 | public void onAction(List permissions) {
72 | // toast(R.string.successfully);
73 | if (listener != null) {
74 | listener.success();
75 | }
76 | }
77 | })
78 | .onDenied(new Action() {
79 | @Override
80 | public void onAction(@NonNull List permissions) {
81 | Toast.makeText(BaseActivity.this, "失败", Toast.LENGTH_SHORT);
82 | if (listener != null) {
83 | listener.failed(null);
84 | }
85 | if (AndPermission.hasAlwaysDeniedPermission(BaseActivity.this, permissions)) {
86 | mSetting.showSetting(permissions);
87 | }
88 | }
89 | })
90 | .start();
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/activity/ContactListActivity.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.activity;
2 |
3 | import android.content.DialogInterface;
4 | import android.content.Intent;
5 | import android.os.AsyncTask;
6 |
7 | import android.os.Bundle;
8 |
9 | import android.view.LayoutInflater;
10 | import android.view.Menu;
11 | import android.view.MenuItem;
12 | import android.view.View;
13 | import android.widget.EditText;
14 | import android.widget.TextView;
15 | import android.widget.Toast;
16 |
17 | import androidx.appcompat.app.ActionBar;
18 | import androidx.appcompat.app.AlertDialog;
19 | import androidx.appcompat.app.AppCompatActivity;
20 | import androidx.recyclerview.widget.LinearLayoutManager;
21 | import androidx.recyclerview.widget.RecyclerView;
22 |
23 | import com.hosea.messagerelayer.R;
24 | import com.hosea.messagerelayer.adapter.ContactListAdapter;
25 | import com.hosea.messagerelayer.adapter.decoration.ContactDecoration;
26 | import com.hosea.messagerelayer.bean.Contact;
27 | import com.hosea.messagerelayer.confing.Constant;
28 | import com.hosea.messagerelayer.utils.ContactManager;
29 | import com.hosea.messagerelayer.utils.db.DataBaseManager;
30 |
31 | import java.util.ArrayList;
32 |
33 | public class ContactListActivity extends AppCompatActivity {
34 |
35 | private RecyclerView mContactRecycler;
36 | private ContactListAdapter mContactListAdapter;
37 | private ArrayList mContactList;
38 |
39 | @Override
40 | protected void onCreate(Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 | setContentView(R.layout.activity_contact_list);
43 | initActionbar();
44 | mContactRecycler = (RecyclerView) findViewById(R.id.list_contact);
45 | initRecyclerView();
46 | }
47 |
48 | private void initActionbar() {
49 | ActionBar actionBar = getSupportActionBar();
50 | actionBar.setDisplayHomeAsUpEnabled(true);
51 | }
52 |
53 | @Override
54 | public boolean onCreateOptionsMenu(Menu menu) {
55 |
56 | menu.add("查找").setIcon(R.mipmap.ic_find)
57 | .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
58 | @Override
59 | public boolean onMenuItemClick(MenuItem item) {
60 | showFindDialog();
61 | return true;
62 | }
63 | }).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
64 |
65 | menu.add("确定").setIcon(R.mipmap.ic_save)
66 | .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
67 | @Override
68 | public boolean onMenuItemClick(MenuItem item) {
69 | updateSelect();
70 | return true;
71 | }
72 | }).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
73 |
74 | return super.onCreateOptionsMenu(menu);
75 | }
76 |
77 | private void showFindDialog() {
78 | AlertDialog.Builder builder = new AlertDialog.Builder(this);
79 | View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit, null, false);
80 | final EditText editText = (EditText) view.findViewById(R.id.dialog_edit);
81 | TextView textView = (TextView) view.findViewById(R.id.dialog_title);
82 | textView.setText("请输入要查找的联系人全名");
83 |
84 | builder.setView(view);
85 | builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
86 | @Override
87 | public void onClick(DialogInterface dialog, int which) {
88 |
89 | }
90 | });
91 |
92 | builder.setPositiveButton("查找", new DialogInterface.OnClickListener() {
93 | @Override
94 | public void onClick(DialogInterface dialog, int which) {
95 | String name = editText.getText().toString();
96 | if (name.length() != 0){
97 | findName(name);
98 | }
99 | }
100 | });
101 |
102 | builder.show();
103 | }
104 |
105 | /**
106 | * 根据名字查找
107 | */
108 | private void findName(String name) {
109 | int length = mContactList.size();
110 | for (int i=0;i contactList = mContactListAdapter.getSelectedList();
135 | dataBaseManager.addContactList(contactList);
136 | dataBaseManager.closeHelper();
137 | Intent intent = new Intent(this, SelectedContactActivity.class);
138 | intent.putExtra(Constant.EXTRA_DATA_CHANGE, true);
139 | startActivity(intent);
140 | finish();
141 | }
142 |
143 | /**
144 | * 初始化RecyclerView
145 | * 从系统数据库中拿出所有联系人
146 | */
147 | private void initRecyclerView() {
148 | mContactRecycler.addItemDecoration(new ContactDecoration());
149 | mContactRecycler.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
150 | new AsyncTask>() {
151 |
152 | @Override
153 | protected ArrayList doInBackground(Void... params) {
154 | mContactList = ContactManager.getContactList(ContactListActivity.this);
155 | return mContactList;
156 | }
157 |
158 | @Override
159 | protected void onPostExecute(ArrayList contacts) {
160 | mContactListAdapter = new ContactListAdapter(ContactListActivity.this, contacts);
161 | mContactRecycler.setAdapter(mContactListAdapter);
162 | }
163 | }.execute();
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/activity/KeywordActivity.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.activity;
2 |
3 | import android.content.DialogInterface;
4 |
5 | import android.os.Bundle;
6 | import android.view.LayoutInflater;
7 | import android.view.Menu;
8 | import android.view.MenuItem;
9 | import android.view.View;
10 | import android.widget.EditText;
11 | import android.widget.TextView;
12 | import android.widget.Toast;
13 |
14 | import androidx.appcompat.app.ActionBar;
15 | import androidx.appcompat.app.AlertDialog;
16 | import androidx.appcompat.app.AppCompatActivity;
17 |
18 | import com.google.android.flexbox.FlexboxLayout;
19 | import com.hosea.messagerelayer.R;
20 | import com.hosea.messagerelayer.utils.NativeDataManager;
21 |
22 | import java.util.HashSet;
23 | import java.util.Set;
24 |
25 |
26 | public class KeywordActivity extends AppCompatActivity {
27 |
28 | private FlexboxLayout mFlexboxLayout;
29 | private HashSet mTextSet;
30 | private NativeDataManager mNativeDataManager;
31 |
32 | @Override
33 | protected void onCreate(Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 | setContentView(R.layout.activity_keyword);
36 | initActionbar();
37 |
38 | mNativeDataManager = new NativeDataManager(this);
39 | mTextSet = (HashSet) mNativeDataManager.getKeywordSet();
40 |
41 | this.mFlexboxLayout = (FlexboxLayout) findViewById(R.id.layout_flexbox);
42 | initFlexboxLayout();
43 | }
44 |
45 | private void initActionbar(){
46 | ActionBar actionBar = getSupportActionBar();
47 | actionBar.setDisplayHomeAsUpEnabled(true);
48 | }
49 |
50 | @Override
51 | public boolean onCreateOptionsMenu(Menu menu) {
52 | menu.add("添加").setIcon(R.mipmap.ic_add)
53 | .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
54 | @Override
55 | public boolean onMenuItemClick(MenuItem item) {
56 | showAddDialog();
57 | return true;
58 | }
59 | }).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
60 | return super.onCreateOptionsMenu(menu);
61 | }
62 |
63 | @Override
64 | public boolean onOptionsItemSelected(MenuItem item) {
65 | if(item.getItemId() == android.R.id.home){
66 | finish();
67 | }
68 | return super.onOptionsItemSelected(item);
69 | }
70 |
71 | private void showAddDialog() {
72 | AlertDialog.Builder builder = new AlertDialog.Builder(this);
73 | View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit,null,false);
74 | TextView textView = (TextView) view.findViewById(R.id.dialog_title);
75 | final EditText editText = (EditText) view.findViewById(R.id.dialog_edit);
76 |
77 | textView.setText("请输入关键字");
78 | builder.setView(view);
79 | builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
80 | @Override
81 | public void onClick(DialogInterface dialog, int which) {
82 | String keyword = editText.getText().toString();
83 | if(keyword.length()!=0){
84 | Set set = new HashSet<>(mNativeDataManager.getKeywordSet());
85 | mFlexboxLayout.addView(createItemView(keyword,set.size()));
86 | set.add(keyword);
87 | mNativeDataManager.setKeywordSet(set);
88 | }else{
89 | Toast.makeText(KeywordActivity.this,"请输入有效字符",Toast.LENGTH_LONG).show();
90 | }
91 | }
92 | });
93 |
94 | builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
95 | @Override
96 | public void onClick(DialogInterface dialog, int which) {
97 |
98 | }
99 | });
100 | builder.show();
101 | }
102 |
103 | private void initFlexboxLayout() {
104 | int i = 0;
105 | for (String keyword:mTextSet) {
106 | mFlexboxLayout.addView(createItemView(keyword,i++));
107 | }
108 | }
109 |
110 | private View createItemView(String text, int index) {
111 | View view = LayoutInflater.from(this).inflate(R.layout.item_keyword,null,false);
112 | TextView textView = (TextView) view.findViewById(R.id.text_keyword);
113 | textView.setText(text);
114 | initBackground(textView, index);
115 | textView.setCompoundDrawablesWithIntrinsicBounds(null, null
116 | , getResources().getDrawable(R.mipmap.ic_del), null);
117 |
118 | return view;
119 | }
120 |
121 | private void initBackground(TextView textView, int index) {
122 | switch (index % 4) {
123 | case 0:
124 | textView.setBackgroundResource(R.drawable.bg_keyword_one);
125 | break;
126 | case 1:
127 | textView.setBackgroundResource(R.drawable.bg_keyword_two);
128 | break;
129 | case 2:
130 | textView.setBackgroundResource(R.drawable.bg_keyword_three);
131 | break;
132 | case 3:
133 | textView.setBackgroundResource(R.drawable.bg_keyword_four);
134 | break;
135 | }
136 | }
137 |
138 | /**
139 | * 点击删除其View
140 | * @param view
141 | */
142 | public void removeClick(View view){
143 | TextView textView = (TextView) view;
144 | String keyword = textView.getText().toString();
145 | Set set = new HashSet<>(mNativeDataManager.getKeywordSet());
146 | set.remove(keyword);
147 | mNativeDataManager.setKeywordSet(set);
148 |
149 | mFlexboxLayout.removeView((View) view.getParent());
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/activity/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.activity;
2 |
3 | import android.content.Intent;
4 |
5 | import android.os.Bundle;
6 | import android.view.Menu;
7 | import android.view.MenuItem;
8 | import android.view.View;
9 | import android.widget.RelativeLayout;
10 | import android.widget.Toast;
11 |
12 | import com.hosea.messagerelayer.R;
13 | import com.hosea.messagerelayer.listener.ICustomCompletedListener;
14 | import com.hosea.messagerelayer.service.ForegroundService;
15 | import com.hosea.messagerelayer.utils.NativeDataManager;
16 |
17 | import com.yanzhenjie.permission.Permission;
18 |
19 | public class MainActivity extends BaseActivity implements View.OnClickListener {
20 |
21 | private RelativeLayout mSmsLayout, mEmailLayout, mRuleLayout;
22 | private NativeDataManager mNativeDataManager;
23 |
24 | @Override
25 | protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setContentView(R.layout.activity_main);
28 |
29 |
30 | mNativeDataManager = new NativeDataManager(this);
31 | initView();
32 | requestPermission(new ICustomCompletedListener() {
33 | @Override
34 | public void success() {
35 | Intent serviceIntent = new Intent(MainActivity.this, ForegroundService.class);
36 | startService(serviceIntent);
37 | }
38 |
39 | @Override
40 | public void failed(String msg) {
41 | Toast.makeText(MainActivity.this, "不给权限没法完...再见!", Toast.LENGTH_LONG).show();
42 | finish();
43 | }
44 | }, Permission.READ_SMS, Permission.RECEIVE_SMS, Permission.READ_CONTACTS, Permission.READ_PHONE_STATE, Permission.SEND_SMS);
45 |
46 |
47 | }
48 |
49 | @Override
50 | public boolean onCreateOptionsMenu(Menu menu) {
51 | Boolean isReceiver = mNativeDataManager.getReceiver();
52 | final MenuItem menuItem = menu.add("开关");
53 | if (isReceiver) {
54 | menuItem.setIcon(R.mipmap.ic_send_on);
55 | } else {
56 | menuItem.setIcon(R.mipmap.ic_send_off);
57 | }
58 |
59 | menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
60 | @Override
61 | public boolean onMenuItemClick(MenuItem item) {
62 | Boolean receiver = mNativeDataManager.getReceiver();
63 | if (receiver) {
64 | mNativeDataManager.setReceiver(false);
65 | menuItem.setIcon(R.mipmap.ic_send_off);
66 | Toast.makeText(MainActivity.this, "总闸已关闭", Toast.LENGTH_SHORT).show();
67 | } else {
68 | mNativeDataManager.setReceiver(true);
69 | menuItem.setIcon(R.mipmap.ic_send_on);
70 | // IntentWrapper.whiteListMatters(MainActivity.this, "轨迹跟踪服务的持续运行");
71 | Toast.makeText(MainActivity.this, "总闸已开启", Toast.LENGTH_SHORT).show();
72 | }
73 | return true;
74 | }
75 | }).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
76 |
77 | menu.add("关于").setIcon(R.mipmap.ic_about)
78 | .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
79 | @Override
80 | public boolean onMenuItemClick(MenuItem item) {
81 | startActivity(new Intent(MainActivity.this, AboutActivity.class));
82 | return false;
83 | }
84 | }).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
85 |
86 | return super.onCreateOptionsMenu(menu);
87 | }
88 |
89 | private void initView() {
90 | mSmsLayout = (RelativeLayout) findViewById(R.id.sms_relay_layout);
91 | mEmailLayout = (RelativeLayout) findViewById(R.id.email_relay_layout);
92 | mRuleLayout = (RelativeLayout) findViewById(R.id.rule_layout);
93 | // mWeChatLayout = (RelativeLayout) findViewById(R.id.wechat_relay_layout);
94 |
95 | mSmsLayout.setOnClickListener(this);
96 | mEmailLayout.setOnClickListener(this);
97 | mRuleLayout.setOnClickListener(this);
98 | // mWeChatLayout.setOnClickListener(this);
99 | }
100 |
101 | @Override
102 | public void onClick(View v) {
103 | switch (v.getId()) {
104 | case R.id.sms_relay_layout:
105 | startActivity(new Intent(this, SmsRelayerActivity.class));
106 | break;
107 | case R.id.email_relay_layout:
108 | startActivity(new Intent(this, EmailRelayerActivity.class));
109 | break;
110 | case R.id.rule_layout:
111 | startActivity(new Intent(this, RuleActivity.class));
112 | break;
113 | // case R.id.wechat_relay_layout:
114 | // startActivity(new Intent(this, WeChatConfigurationAct.class));
115 | // break;
116 | }
117 | }
118 |
119 |
120 | }
121 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/activity/RuleActivity.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.activity;
2 |
3 | import android.content.DialogInterface;
4 | import android.content.Intent;
5 |
6 | import android.os.Bundle;
7 | import android.view.LayoutInflater;
8 | import android.view.MenuItem;
9 | import android.view.View;
10 | import android.widget.EditText;
11 | import android.widget.RelativeLayout;
12 | import android.widget.TextView;
13 |
14 | import androidx.appcompat.app.ActionBar;
15 | import androidx.appcompat.app.AlertDialog;
16 | import androidx.appcompat.app.AppCompatActivity;
17 |
18 | import com.hosea.messagerelayer.R;
19 | import com.hosea.messagerelayer.confing.Constant;
20 | import com.hosea.messagerelayer.utils.NativeDataManager;
21 |
22 | public class RuleActivity extends AppCompatActivity implements View.OnClickListener {
23 |
24 | private RelativeLayout mMoblieRuleLayout, mKeywordRuleLayout, mPrefixRuleLayout, mSuffixRuleLayout, mSmsListRuleLayout, mBlackRule;
25 | private NativeDataManager mNativeDataManager;
26 | private TextView mPrefixText, mSuffixText;
27 |
28 | @Override
29 | protected void onCreate(Bundle savedInstanceState) {
30 |
31 | super.onCreate(savedInstanceState);
32 | setContentView(R.layout.activity_rule);
33 |
34 | initActionbar();
35 | mNativeDataManager = new NativeDataManager(this);
36 | initView();
37 | initData();
38 | initListener();
39 | }
40 |
41 | private void initActionbar() {
42 | ActionBar actionBar = getSupportActionBar();
43 | actionBar.setDisplayHomeAsUpEnabled(true);
44 | }
45 |
46 | private void initView() {
47 | mMoblieRuleLayout = (RelativeLayout) findViewById(R.id.layout_rule_mobile);
48 | mKeywordRuleLayout = (RelativeLayout) findViewById(R.id.layout_rule_keyword);
49 |
50 | mPrefixRuleLayout = (RelativeLayout) findViewById(R.id.layout_rule_prefix);
51 | mSuffixRuleLayout = (RelativeLayout) findViewById(R.id.layout_rule_suffix);
52 | mSmsListRuleLayout = (RelativeLayout) findViewById(R.id.layout_rule_sms_list);
53 | mBlackRule = (RelativeLayout) findViewById(R.id.layout_rule_black);
54 |
55 | mPrefixText = (TextView) findViewById(R.id.text_prefix);
56 | mSuffixText = (TextView) findViewById(R.id.text_suffix);
57 | }
58 |
59 | private void initListener() {
60 | mMoblieRuleLayout.setOnClickListener(this);
61 | mKeywordRuleLayout.setOnClickListener(this);
62 |
63 | mPrefixRuleLayout.setOnClickListener(this);
64 | mSuffixRuleLayout.setOnClickListener(this);
65 | mBlackRule.setOnClickListener(this);
66 | mSmsListRuleLayout.setOnClickListener(this);
67 | }
68 |
69 | private void initData() {
70 | String prefix = mNativeDataManager.getContentPrefix();
71 | if (prefix != null) {
72 | mPrefixText.setText(prefix);
73 | }
74 | String suffix = mNativeDataManager.getContentSuffix();
75 | if (suffix != null) {
76 | mSuffixText.setText(suffix);
77 | }
78 | }
79 |
80 | @Override
81 | public boolean onOptionsItemSelected(MenuItem item) {
82 | if (item.getItemId() == android.R.id.home) {
83 | finish();
84 | }
85 | return super.onOptionsItemSelected(item);
86 | }
87 |
88 | @Override
89 | public void onClick(View v) {
90 | switch (v.getId()) {
91 | case R.id.layout_rule_mobile:
92 | startActivity(new Intent(this, SelectedContactActivity.class));
93 |
94 | break;
95 | case R.id.layout_rule_keyword:
96 | startActivity(new Intent(this, KeywordActivity.class));
97 | break;
98 | case R.id.layout_rule_prefix:
99 | showEditDialog("请输入要附加的内容前缀", Constant.KEY_CONTENT_PREFIX);
100 | break;
101 | case R.id.layout_rule_suffix:
102 | showEditDialog("请输入要附加的内容后缀", Constant.KEY_CONTENT_SUFFIX);
103 | break;
104 | case R.id.layout_rule_sms_list:
105 | startActivity(new Intent(this, SmsActivity.class));
106 |
107 | break;
108 | case R.id.layout_rule_black:
109 | startActivity(new Intent(this, SmsBlackActivity.class));
110 | break;
111 | }
112 | }
113 |
114 | private void showEditDialog(String title, final String key) {
115 | AlertDialog.Builder buileder = new AlertDialog.Builder(this);
116 | View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit, null, false);
117 | TextView textTitle = (TextView) view.findViewById(R.id.dialog_title);
118 | final EditText editText = (EditText) view.findViewById(R.id.dialog_edit);
119 |
120 | textTitle.setText(title);
121 | buileder.setView(view);
122 | buileder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
123 | @Override
124 | public void onClick(DialogInterface dialog, int which) {
125 |
126 | }
127 | });
128 |
129 | buileder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
130 | @Override
131 | public void onClick(DialogInterface dialog, int which) {
132 | String text = editText.getText().toString();
133 | if (key.equals(Constant.KEY_CONTENT_PREFIX)) {
134 | mNativeDataManager.setContentPrefix(text);
135 | mPrefixText.setText(text);
136 | } else {
137 | mNativeDataManager.setContentSuffix(text);
138 | mSuffixText.setText(text);
139 | }
140 | }
141 | });
142 |
143 | buileder.show();
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/activity/SelectedContactActivity.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.activity;
2 |
3 | import android.content.DialogInterface;
4 | import android.content.Intent;
5 |
6 | import android.os.Bundle;
7 |
8 | import android.text.InputType;
9 | import android.view.LayoutInflater;
10 | import android.view.MenuItem;
11 | import android.view.View;
12 | import android.widget.Button;
13 | import android.widget.EditText;
14 | import android.widget.TextView;
15 | import android.widget.Toast;
16 |
17 | import androidx.appcompat.app.ActionBar;
18 | import androidx.appcompat.app.AlertDialog;
19 | import androidx.appcompat.app.AppCompatActivity;
20 | import androidx.recyclerview.widget.LinearLayoutManager;
21 | import androidx.recyclerview.widget.RecyclerView;
22 |
23 | import com.hosea.messagerelayer.R;
24 | import com.hosea.messagerelayer.adapter.ContactSelectedAdapter;
25 | import com.hosea.messagerelayer.adapter.decoration.ContactDecoration;
26 | import com.hosea.messagerelayer.bean.Contact;
27 | import com.hosea.messagerelayer.confing.Constant;
28 | import com.hosea.messagerelayer.utils.db.DataBaseManager;
29 |
30 | import java.util.ArrayList;
31 |
32 | public class SelectedContactActivity extends AppCompatActivity implements View.OnClickListener {
33 |
34 | private RecyclerView mSelcetedContactList;
35 | private Button mAddContactButton, mAddOneContact;
36 | private ContactSelectedAdapter mContactAdapter;
37 |
38 | @Override
39 | protected void onCreate(Bundle savedInstanceState) {
40 | super.onCreate(savedInstanceState);
41 | setContentView(R.layout.activity_selected_contact);
42 | initActionbar();
43 |
44 | mSelcetedContactList = (RecyclerView) findViewById(R.id.list_selected_contact);
45 | mAddContactButton = (Button) findViewById(R.id.button_add_contact);
46 | mAddOneContact = (Button) findViewById(R.id.button_add_one);
47 |
48 | mAddContactButton.setOnClickListener(this);
49 | mAddOneContact.setOnClickListener(this);
50 |
51 | initRecyclerView();
52 | }
53 |
54 | @Override
55 | protected void onNewIntent(Intent intent) {
56 | if(intent.getBooleanExtra(Constant.EXTRA_DATA_CHANGE,false)){
57 | mContactAdapter.notifyUpdata(getContactList());
58 | }
59 | super.onNewIntent(intent);
60 | }
61 |
62 | @Override
63 | public boolean onOptionsItemSelected(MenuItem item) {
64 | if(item.getItemId() == android.R.id.home){
65 | finish();
66 | }
67 | return super.onOptionsItemSelected(item);
68 | }
69 |
70 | private void initActionbar(){
71 | ActionBar actionBar = getSupportActionBar();
72 | actionBar.setDisplayHomeAsUpEnabled(true);
73 | }
74 |
75 | /**
76 | * 初始化RecyclerView
77 | * 从保存被选中联系人的数据库中拿出所有数据,并填充到RecyclerView
78 | */
79 | private void initRecyclerView() {
80 | mSelcetedContactList.addItemDecoration(new ContactDecoration());
81 | mSelcetedContactList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
82 |
83 | ArrayList selectedList = getContactList();
84 | mContactAdapter = new ContactSelectedAdapter(this, selectedList);
85 | mSelcetedContactList.setAdapter(mContactAdapter);
86 |
87 | }
88 |
89 | private ArrayList getContactList() {
90 | DataBaseManager dataBaseManager = new DataBaseManager(this);
91 | ArrayList selectedList = dataBaseManager.getAllContact();
92 | dataBaseManager.closeHelper();
93 | return selectedList;
94 | }
95 |
96 | @Override
97 | public void onClick(View v) {
98 | switch (v.getId()) {
99 | case R.id.button_add_contact:
100 | startActivity(new Intent(this, ContactListActivity.class));
101 | break;
102 | case R.id.button_add_one:
103 | showAddOneDialog();
104 | break;
105 | }
106 | }
107 |
108 |
109 | private void showAddOneDialog() {
110 | LayoutInflater inflater = LayoutInflater.from(this);
111 | View view = inflater.inflate(R.layout.dialog_email_account, null, false);
112 | final EditText textName = (EditText) view.findViewById(R.id.editText_account);
113 | final EditText textNum = (EditText) view.findViewById(R.id.editText_password);
114 | TextView title = (TextView) view.findViewById(R.id.dialog_title);
115 |
116 | textName.setHint("名称");
117 | textNum.setHint("电话号");
118 | title.setText("请输入名称和手机号");
119 | textNum.setInputType(InputType.TYPE_TEXT_VARIATION_PHONETIC);
120 |
121 | AlertDialog.Builder builder = new AlertDialog.Builder(this);
122 | builder.setView(view);
123 |
124 | builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
125 | @Override
126 | public void onClick(DialogInterface dialog, int which) {
127 |
128 | }
129 | });
130 |
131 | builder.setPositiveButton("保存", new DialogInterface.OnClickListener() {
132 | @Override
133 | public void onClick(DialogInterface dialog, int which) {
134 | String name = textName.getText().toString();
135 | String num = textNum.getText().toString();
136 | if (name.length() != 0 && num.length() != 0) {
137 | DataBaseManager dataBaseManager = new DataBaseManager(SelectedContactActivity.this);
138 | Contact contact = new Contact(name,num);
139 | dataBaseManager.addContact(contact);
140 | dataBaseManager.closeHelper();
141 | mContactAdapter.notifyUpdata(getContactList());
142 | }else{
143 | Toast.makeText(SelectedContactActivity.this,"名称或手机号不能为空",Toast.LENGTH_LONG).show();
144 | }
145 | }
146 | });
147 |
148 | builder.show();
149 |
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/activity/SmsActivity.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.activity;
2 |
3 | import android.content.ContentResolver;
4 | import android.content.DialogInterface;
5 | import android.database.Cursor;
6 | import android.net.Uri;
7 | import android.os.Bundle;
8 |
9 | import android.util.Log;
10 | import android.view.View;
11 | import android.widget.AdapterView;
12 | import android.widget.ListView;
13 | import android.widget.Toast;
14 |
15 | import androidx.annotation.Nullable;
16 | import androidx.appcompat.app.AlertDialog;
17 |
18 | import com.blankj.utilcode.util.LogUtils;
19 | import com.hosea.messagerelayer.R;
20 | import com.hosea.messagerelayer.adapter.SmsAdapter;
21 | import com.hosea.messagerelayer.bean.SmsBean;
22 | import com.hosea.messagerelayer.utils.db.DataBaseManager;
23 |
24 | import java.util.ArrayList;
25 | import java.util.HashMap;
26 | import java.util.Map;
27 |
28 |
29 | /**
30 | * Created by heliu on 2018/7/17.
31 | */
32 |
33 | public class SmsActivity extends BaseActivity {
34 |
35 | private ArrayList mData = new ArrayList<>();
36 | private ListView lv;
37 | private SmsAdapter adapter;
38 | private DataBaseManager dataBaseManager;
39 |
40 | @Override
41 | protected void onCreate(@Nullable Bundle savedInstanceState) {
42 | super.onCreate(savedInstanceState);
43 | setContentView(R.layout.activity_sms_list);
44 |
45 | dataBaseManager = new DataBaseManager(this);
46 | initView();
47 | getSmsFromPhone();
48 | initAdapter();
49 |
50 | }
51 |
52 | private void initAdapter() {
53 | adapter = new SmsAdapter(mData, this);
54 | lv.setAdapter(adapter);
55 |
56 | }
57 |
58 | private void initView() {
59 |
60 | lv = (ListView) findViewById(R.id.lv);
61 | lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
62 | @Override
63 | public void onItemClick(AdapterView> parent, View view, final int position, long id) {
64 | new AlertDialog.Builder(SmsActivity.this).setTitle("是否拉黑").setMessage("拉黑后将不转发对方短信").setPositiveButton("拉黑", new DialogInterface.OnClickListener() {
65 | @Override
66 | public void onClick(DialogInterface dialog, int which) {
67 | SmsBean smsBean = mData.get(position);
68 | dataBaseManager.addSMSIntercept(smsBean);
69 | Toast.makeText(SmsActivity.this, "添加成功", Toast.LENGTH_SHORT).show();
70 | }
71 | }).setNegativeButton("否", null).show();
72 |
73 | }
74 | });
75 | }
76 |
77 | private Uri SMS_INBOX = Uri.parse("content://sms/");
78 |
79 | public void getSmsFromPhone() {
80 | ContentResolver cr = getContentResolver();
81 | Map map = new HashMap();
82 | String[] projection = new String[]{"_id", "address", "person", "body", "date", "type"};
83 | Cursor cur = cr.query(SMS_INBOX, projection, null, null, "date desc");
84 | if (null == cur) {
85 | LogUtils.i("ooc", "************cur == null");
86 | return;
87 | }
88 | while (cur.moveToNext()) {
89 | String number = cur.getString(cur.getColumnIndex("address"));//手机号
90 | String name = cur.getString(cur.getColumnIndex("person"));//联系人姓名列表
91 | String body = cur.getString(cur.getColumnIndex("body"));//短信内容
92 | //简单的进行判断.
93 | if (!map.containsKey(number)) {
94 | map.put(number, number);
95 | SmsBean smsBean = new SmsBean();
96 | smsBean.setContent(body);
97 | smsBean.setName(name);
98 | smsBean.setPhone(number);
99 | mData.add(smsBean);
100 | }
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/activity/SmsBlackActivity.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.activity;
2 |
3 | import android.app.ListActivity;
4 | import android.content.ContentResolver;
5 | import android.content.DialogInterface;
6 | import android.database.Cursor;
7 | import android.net.Uri;
8 | import android.os.Bundle;
9 |
10 | import android.util.Log;
11 | import android.view.View;
12 | import android.widget.AdapterView;
13 | import android.widget.ListView;
14 | import android.widget.Toast;
15 |
16 | import androidx.annotation.Nullable;
17 | import androidx.appcompat.app.AlertDialog;
18 |
19 | import com.blankj.utilcode.util.LogUtils;
20 | import com.hosea.messagerelayer.R;
21 | import com.hosea.messagerelayer.adapter.SmsAdapter;
22 | import com.hosea.messagerelayer.bean.SmsBean;
23 | import com.hosea.messagerelayer.utils.db.DataBaseManager;
24 |
25 | import java.util.ArrayList;
26 |
27 |
28 | /**
29 | * Created by heliu on 2018/7/17.
30 | */
31 |
32 | public class SmsBlackActivity extends ListActivity {
33 |
34 | private ArrayList mData = new ArrayList<>();
35 | private ListView lv;
36 | private SmsAdapter adapter;
37 | private DataBaseManager dataBaseManager;
38 |
39 | @Override
40 | protected void onCreate(@Nullable Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 | initAdapter();
43 |
44 | }
45 |
46 |
47 | private void initAdapter() {
48 | dataBaseManager = new DataBaseManager(this);
49 | ArrayList smsIntercept = dataBaseManager.getSmsIntercept();
50 | mData.addAll(smsIntercept);
51 | adapter = new SmsAdapter(mData, this);
52 | setListAdapter(adapter);
53 |
54 |
55 | }
56 |
57 | @Override
58 | protected void onListItemClick(ListView l, View v, final int position, long id) {
59 | new AlertDialog.Builder(SmsBlackActivity.this).setTitle("是否取消拉黑").setMessage("重新收到对方短信").setPositiveButton("取消拉黑", new DialogInterface.OnClickListener() {
60 | @Override
61 | public void onClick(DialogInterface dialog, int which) {
62 | SmsBean smsBean = mData.get(position);
63 | dataBaseManager.deleteSmsFromMobile(smsBean.getPhone());
64 | Toast.makeText(SmsBlackActivity.this, "移除成功", Toast.LENGTH_SHORT).show();
65 | mData.remove(position);
66 | adapter.notifyDataSetChanged();
67 | }
68 | }).setNegativeButton("否", null).show();
69 |
70 | }
71 |
72 | private void initView() {
73 |
74 | lv = (ListView) findViewById(R.id.lv);
75 | lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
76 | @Override
77 | public void onItemClick(AdapterView> parent, View view, final int position, long id) {
78 |
79 | }
80 | });
81 | }
82 |
83 | private Uri SMS_INBOX = Uri.parse("content://sms/");
84 |
85 | public void getSmsFromPhone() {
86 | ContentResolver cr = getContentResolver();
87 | String[] projection = new String[]{"_id", "address", "person", "body", "date", "type"};
88 | Cursor cur = cr.query(SMS_INBOX, projection, null, null, "date desc");
89 | if (null == cur) {
90 | LogUtils.i("ooc", "************cur == null");
91 | return;
92 | }
93 | while (cur.moveToNext()) {
94 | String number = cur.getString(cur.getColumnIndex("address"));//手机号
95 | String name = cur.getString(cur.getColumnIndex("person"));//联系人姓名列表
96 | String body = cur.getString(cur.getColumnIndex("body"));//短信内容
97 | //至此就获得了短信的相关的内容, 以下是把短信加入map中,构建listview,非必要。
98 | SmsBean smsBean = new SmsBean();
99 | smsBean.setContent(body);
100 | smsBean.setName(name);
101 | smsBean.setPhone(number);
102 | mData.add(smsBean);
103 |
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/activity/SmsRelayerActivity.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.activity;
2 |
3 | import android.content.DialogInterface;
4 |
5 | import android.os.Bundle;
6 | import android.view.LayoutInflater;
7 | import android.view.MenuItem;
8 | import android.view.View;
9 | import android.widget.CompoundButton;
10 | import android.widget.EditText;
11 | import android.widget.RelativeLayout;
12 | import android.widget.Switch;
13 | import android.widget.TextView;
14 | import android.widget.Toast;
15 |
16 | import androidx.appcompat.app.ActionBar;
17 | import androidx.appcompat.app.AlertDialog;
18 |
19 | import com.hosea.messagerelayer.R;
20 | import com.hosea.messagerelayer.listener.ICustomCompletedListener;
21 | import com.hosea.messagerelayer.utils.NativeDataManager;
22 |
23 | import com.yanzhenjie.permission.Permission;
24 |
25 |
26 | public class SmsRelayerActivity extends BaseActivity
27 | implements CompoundButton.OnCheckedChangeListener, View.OnClickListener {
28 |
29 | private Switch mSmsSwitchSelf;
30 | private RelativeLayout mMobileRelativeSelf, mMobileRelativeRule;
31 | private TextView mMobileTextSelf, mMobileTextRule;
32 |
33 | private NativeDataManager mNativeDataManager;
34 |
35 | @Override
36 | protected void onCreate(Bundle savedInstanceState) {
37 | super.onCreate(savedInstanceState);
38 | setContentView(R.layout.activity_sms_relayer);
39 | initActionbar();
40 |
41 | init();
42 | }
43 |
44 | @Override
45 | public boolean onOptionsItemSelected(MenuItem item) {
46 | if (item.getItemId() == android.R.id.home) {
47 | finish();
48 | }
49 | return super.onOptionsItemSelected(item);
50 | }
51 |
52 | private void initActionbar() {
53 | ActionBar actionBar = getSupportActionBar();
54 | actionBar.setDisplayHomeAsUpEnabled(true);
55 | }
56 |
57 | private void init() {
58 | mNativeDataManager = new NativeDataManager(this);
59 |
60 | initView();
61 | initData();
62 | initListener();
63 |
64 | }
65 |
66 | private void initView() {
67 | // mSmsSwitch = (Switch) findViewById(R.id.switch_sms);
68 | // mMobileRelative = (RelativeLayout) findViewById(R.id.layout_mobile);
69 | // mMobileText = (TextView) findViewById(R.id.text_mobile);
70 |
71 |
72 | mSmsSwitchSelf = (Switch) findViewById(R.id.switch_self);
73 | mMobileRelativeSelf = (RelativeLayout) findViewById(R.id.layout_self);
74 | mMobileTextSelf = (TextView) findViewById(R.id.text_self);
75 |
76 | mMobileRelativeRule = (RelativeLayout) findViewById(R.id.layout_rule);
77 | mMobileTextRule = (TextView) findViewById(R.id.text_rule);
78 | }
79 |
80 | private void initData() {
81 | // if (mNativeDataManager.getSmsRelay()) {
82 | // mSmsSwitch.setChecked(true);
83 | // } else {
84 | // mSmsSwitch.setChecked(false);
85 | // }
86 |
87 | if (mNativeDataManager.getInnerRelay()) {
88 | mSmsSwitchSelf.setChecked(true);
89 | } else {
90 | mSmsSwitchSelf.setChecked(false);
91 | }
92 | // mMobileText.setText(mNativeDataManager.getObjectMobile());
93 | mMobileTextSelf.setText(mNativeDataManager.getInnerMobile());
94 | mMobileTextRule.setText(mNativeDataManager.getInnerRule());
95 | }
96 |
97 | private void initListener() {
98 | // mSmsSwitch.setOnCheckedChangeListener(this);
99 |
100 | // mMobileRelative.setOnClickListener(this);
101 | mSmsSwitchSelf.setOnCheckedChangeListener(this);
102 |
103 | mMobileRelativeSelf.setOnClickListener(this);
104 | mMobileRelativeRule.setOnClickListener(this);
105 | }
106 |
107 | @Override
108 | public void onCheckedChanged(CompoundButton buttonView, final boolean isChecked) {
109 | switch (buttonView.getId()) {
110 | case R.id.switch_self:
111 | requestPermission(new ICustomCompletedListener() {
112 | @Override
113 | public void success() {
114 | if (mNativeDataManager.getSendSMSHint()) {
115 | Toast.makeText(SmsRelayerActivity.this, "发送短信权限比较特殊.部分手机可能会再下一次开启软件的时候再次请求.如遇这种情况可以在应用程序中手动给允许权限!", Toast.LENGTH_LONG).show();
116 | mNativeDataManager.setSendSMSHint(false);
117 | }
118 | if (buttonView.getId() == R.id.switch_self) {
119 | InnerSmsChecked(isChecked);
120 | } else {
121 | smsChecked(isChecked);
122 | }
123 | }
124 |
125 | @Override
126 | public void failed(String msg) {
127 |
128 | }
129 | }, Permission.SEND_SMS);
130 | break;
131 | }
132 | }
133 |
134 | /**
135 | * 使用短信转发至指定手机号的Switch的事件方法
136 | *
137 | * @param isChecked
138 | */
139 | private void smsChecked(boolean isChecked) {
140 | if (isChecked) {
141 | mNativeDataManager.setSmsRelay(true);
142 | } else {
143 | mNativeDataManager.setSmsRelay(false);
144 | }
145 | }
146 |
147 | /**
148 | * 使用短信转发至指定手机号的Switch的事件方法
149 | *
150 | * @param isChecked
151 | */
152 | private void InnerSmsChecked(boolean isChecked) {
153 | if (isChecked) {
154 | mNativeDataManager.setInnerRelay(true);
155 | } else {
156 | mNativeDataManager.setInnerRelay(false);
157 | }
158 | }
159 |
160 |
161 | @Override
162 | public void onClick(View v) {
163 | switch (v.getId()) {
164 | // case R.id.layout_mobile:
165 | // showEditDialog(v);
166 | // break;
167 | case R.id.layout_self:
168 | showEditDialog(v);
169 | break;
170 | case R.id.layout_rule:
171 | showEditDialog(v);
172 | break;
173 | }
174 | }
175 |
176 | private void showEditDialog(View v) {
177 |
178 | AlertDialog.Builder builder = new AlertDialog.Builder(this);
179 | View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit, null, false);
180 | final EditText mobileEdit = (EditText) view.findViewById(R.id.dialog_edit);
181 | // String mobileText = mMobileText.getText().toString();
182 | // if (!mobileText.equals("点击设置")) {
183 | // mobileEdit.setText(mobileText);
184 | // }
185 |
186 | builder.setView(view);
187 | builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
188 | @Override
189 | public void onClick(DialogInterface dialog, int which) {
190 |
191 | }
192 | });
193 | builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
194 | @Override
195 | public void onClick(DialogInterface dialog, int which) {
196 | if (v.getId() == R.id.layout_self) {
197 | mNativeDataManager.setInnerMobile(mobileEdit.getText().toString());
198 | mMobileTextSelf.setText(mobileEdit.getText());
199 | }else {
200 | mNativeDataManager.setInnerRule(mobileEdit.getText().toString());
201 | mMobileTextRule.setText(mobileEdit.getText());
202 | }
203 |
204 | }
205 | });
206 | builder.show();
207 | }
208 | }
209 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/activity/StartActivity.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.activity;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 |
7 | public class StartActivity extends Activity {
8 |
9 | @Override
10 | protected void onCreate(Bundle savedInstanceState) {
11 | super.onCreate(savedInstanceState);
12 | startActivity(new Intent(this,MainActivity.class));
13 | finish();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/activity/WeChatConfigurationAct.java:
--------------------------------------------------------------------------------
1 | //package com.hl.messagerelayer.activity;
2 | //
3 | //import android.content.Intent;
4 | //import android.content.pm.PackageManager;
5 | //import android.os.Bundle;
6 | //
7 | //import android.view.View;
8 | //import android.widget.CompoundButton;
9 | //import android.widget.Switch;
10 | //
11 | //import androidx.appcompat.app.AppCompatActivity;
12 | //
13 | //import com.hl.messagerelayer.R;
14 | //import com.hl.messagerelayer.utils.NativeDataManager;
15 | //import com.hl.messagerelayer.utils.OpenAccessibilitySettingHelper;
16 | //
17 | ///**
18 | // * Created by heliu on 2018/6/29.
19 | // */
20 | //
21 | //public class WeChatConfigurationAct extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener {
22 | // // EditText edit;
23 | // Switch mWeChatSwith;
24 | // NativeDataManager mNativeDataManager;
25 | //
26 | // @Override
27 | // protected void onCreate(Bundle savedInstanceState) {
28 | // super.onCreate(savedInstanceState);
29 | // setContentView(R.layout.activitiy_wechat);
30 | // mNativeDataManager = new NativeDataManager(this);
31 | // initView();
32 | //
33 | // }
34 | //
35 | // private void initView() {
36 | //// edit = (EditText) findViewById(R.id.edit);
37 | // mWeChatSwith = (Switch) findViewById(R.id.switch_wechat);
38 | // findViewById(R.id.open_accessibility_setting).setOnClickListener(clickListener);
39 | // findViewById(R.id.btn_save).setOnClickListener(clickListener);
40 | //
41 | // if (mNativeDataManager.getWeChatRelay()) {
42 | // mWeChatSwith.setChecked(true);
43 | // } else {
44 | // mWeChatSwith.setChecked(false);
45 | // }
46 | // mWeChatSwith.setOnCheckedChangeListener(this);
47 | // }
48 | //
49 | // @Override
50 | // public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
51 | // switch (buttonView.getId()) {
52 | // case R.id.switch_wechat:
53 | // weChatCheck(isChecked);
54 | // break;
55 | // }
56 | // }
57 | //
58 | //
59 | // /**
60 | // * 使用短信转发至指定手机号的Switch的事件方法
61 | // *
62 | // * @param isChecked
63 | // */
64 | // private void weChatCheck(boolean isChecked) {
65 | // if (isChecked) {
66 | // mNativeDataManager.setWeChatRelay(true);
67 | // } else {
68 | // mNativeDataManager.setWeChatRelay(false);
69 | // }
70 | // }
71 | //
72 | // View.OnClickListener clickListener = new View.OnClickListener() {
73 | // @Override
74 | // public void onClick(View v) {
75 | // switch (v.getId()) {
76 | // case R.id.open_accessibility_setting:
77 | // OpenAccessibilitySettingHelper.jumpToSettingPage(getBaseContext());
78 | // break;
79 | // case R.id.btn_save:
80 | // openWeChatApplication();
81 | // break;
82 | // }
83 | // }
84 | // };
85 | //
86 | //// public boolean checkParams() {
87 | //// if (TextUtils.isEmpty(editIndex.getText().toString())) {
88 | //// Toast.makeText(getBaseContext(), "起始下标不能为空", Toast.LENGTH_SHORT).show();
89 | //// return false;
90 | //// }
91 | ////
92 | //// if (TextUtils.isEmpty(editCount.getText().toString())) {
93 | //// Toast.makeText(getBaseContext(), "图片总数不能为空", Toast.LENGTH_SHORT).show();
94 | //// return false;
95 | //// }
96 | ////
97 | //// if (Integer.valueOf(editCount.getText().toString()) > 9) {
98 | //// Toast.makeText(getBaseContext(), "图片总数不能超过9张", Toast.LENGTH_SHORT).show();
99 | //// return false;
100 | //// }
101 | ////
102 | //// return true;
103 | //// }
104 | //
105 | // private void saveData() {
106 | //
107 | //// if (!checkParams()) {
108 | //// return;
109 | //// }
110 | //
111 | //// int index = Integer.valueOf(editIndex.getText().toString());
112 | //// int count = Integer.valueOf(editCount.getText().toString());
113 | //
114 | //// SharedPreferences sharedPreferences = getSharedPreferences(Constant.WECHAT_STORAGE, Activity.MODE_MULTI_PROCESS);
115 | //// editor.putString(Constant.WECHAT_NAME, edit.getText().toString());
116 | //// editor.putInt(Constant.INDEX, index);
117 | //// editor.putInt(Constant.COUNT, count);
118 | //// if (editor.commit()) {
119 | //// Toast.makeText(getBaseContext(), "保存成功", Toast.LENGTH_LONG).show();
120 | //// openWeChatApplication();//打开微信应用
121 | //// } else {
122 | //// Toast.makeText(getBaseContext(), "保存失败", Toast.LENGTH_LONG).show();
123 | //// }
124 | // }
125 | //
126 | // private void openWeChatApplication() {
127 | // PackageManager packageManager = getBaseContext().getPackageManager();
128 | // Intent it = packageManager.getLaunchIntentForPackage("com.tencent.mm");
129 | // startActivity(it);
130 | // }
131 | //}
132 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/adapter/ContactListAdapter.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.adapter;
2 |
3 | import android.content.Context;
4 |
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import androidx.recyclerview.widget.RecyclerView;
10 |
11 | import com.hosea.messagerelayer.R;
12 | import com.hosea.messagerelayer.adapter.holder.ContactHolder;
13 | import com.hosea.messagerelayer.bean.Contact;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | /**
19 | * Created by WHF on 2017/3/28.
20 | */
21 |
22 | public class ContactListAdapter extends RecyclerView.Adapter {
23 |
24 | private LayoutInflater mInflater;
25 | private List mContactList;
26 | private ArrayList mSelectedList = null;
27 |
28 | public ContactListAdapter(Context context, List contactList) {
29 | this.mInflater = LayoutInflater.from(context);
30 | this.mContactList = contactList;
31 | this.mSelectedList = new ArrayList<>();
32 | }
33 |
34 | private View getItemView(LayoutInflater inflater, ViewGroup parent) {
35 | return inflater.inflate(R.layout.item_contact,parent,false);
36 | }
37 |
38 | @Override
39 | public ContactHolder onCreateViewHolder(ViewGroup parent, int viewType) {
40 | View view = getItemView(mInflater,parent);
41 | ContactHolder holder = new ContactHolder(view);
42 | return holder;
43 | }
44 |
45 | @Override
46 | public void onBindViewHolder(ContactHolder holder, int position) {
47 | final Contact contact = mContactList.get(position);
48 | holder.mContactName.setText(contact.getContactName());
49 | holder.mContactNum.setText(contact.getContactNum());
50 | if(contact.getSelected()==1){
51 | holder.mRightIcon.setImageResource(R.mipmap.ic_selected);
52 | }else {
53 | holder.mRightIcon.setImageResource(R.mipmap.ic_unselected);
54 | }
55 | holder.mItemView.setOnClickListener(new View.OnClickListener() {
56 | @Override
57 | public void onClick(View v) {
58 | if(contact.getSelected()==1){
59 | contact.setSelected(0);
60 | mSelectedList.remove(contact);
61 | }else{
62 | contact.setSelected(1);
63 | mSelectedList.add(contact);
64 | }
65 | notifyDataSetChanged();
66 | }
67 | });
68 | }
69 |
70 | @Override
71 | public int getItemCount() {
72 | return mContactList.size();
73 | }
74 |
75 | public ArrayList getSelectedList(){
76 | return mSelectedList;
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/adapter/ContactSelectedAdapter.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.adapter;
2 |
3 | import android.content.Context;
4 |
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import androidx.recyclerview.widget.RecyclerView;
10 |
11 | import com.hosea.messagerelayer.R;
12 | import com.hosea.messagerelayer.adapter.holder.ContactHolder;
13 | import com.hosea.messagerelayer.bean.Contact;
14 | import com.hosea.messagerelayer.utils.db.DataBaseManager;
15 |
16 | import java.util.List;
17 |
18 | /**
19 | * Created by WHF on 2017/3/28.
20 | */
21 |
22 | public class ContactSelectedAdapter extends RecyclerView.Adapter {
23 |
24 | private LayoutInflater mInflater;
25 | private List mSelectedList;
26 | private Context mContext;
27 |
28 | public ContactSelectedAdapter(Context context, List seletedList) {
29 | this.mContext = context;
30 | this.mInflater = LayoutInflater.from(context);
31 | this.mSelectedList = seletedList;
32 | }
33 |
34 |
35 | private View getItemView(LayoutInflater inflater, ViewGroup parent) {
36 | return inflater.inflate(R.layout.item_selected_contact,parent,false);
37 | }
38 |
39 | @Override
40 | public ContactHolder onCreateViewHolder(ViewGroup parent, int viewType) {
41 | View view = getItemView(mInflater,parent);
42 | ContactHolder holder = new ContactHolder(view);
43 | return holder;
44 | }
45 |
46 | @Override
47 | public void onBindViewHolder(ContactHolder holder, int position) {
48 | final Contact contact = mSelectedList.get(position);
49 | holder.mContactName.setText(contact.getContactName());
50 | holder.mContactNum.setText(contact.getContactNum());
51 |
52 | holder.mRightIcon.setOnClickListener(new View.OnClickListener() {
53 | @Override
54 | public void onClick(View v) {
55 | DataBaseManager dataBaseManager = new DataBaseManager(mContext);
56 | dataBaseManager.deleteContactFromMobile(contact.getContactNum());
57 | mSelectedList.remove(contact);
58 | dataBaseManager.closeHelper();
59 | notifyDataSetChanged();
60 | }
61 | });
62 | }
63 |
64 | @Override
65 | public int getItemCount() {
66 | return mSelectedList.size();
67 | }
68 |
69 | /**
70 | * 外部调用的更新数据
71 | * @param contactList
72 | */
73 | public void notifyUpdata(List contactList){
74 | mSelectedList = contactList;
75 | notifyDataSetChanged();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/adapter/SmsAdapter.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.adapter;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.BaseAdapter;
8 | import android.widget.TextView;
9 |
10 | import com.hosea.messagerelayer.R;
11 | import com.hosea.messagerelayer.bean.SmsBean;
12 |
13 | import java.util.ArrayList;
14 |
15 | /**
16 | * Created by heliu on 2018/7/17.
17 | */
18 |
19 | public class SmsAdapter extends BaseAdapter {
20 | private ArrayList mList;
21 | private Context mCtx;
22 |
23 | public SmsAdapter(ArrayList mList, Context mCtx) {
24 | this.mList = mList;
25 | this.mCtx = mCtx;
26 | }
27 |
28 | @Override
29 | public int getCount() {
30 | return mList.size();
31 | }
32 |
33 | @Override
34 | public SmsBean getItem(int position) {
35 | return mList.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 | SmsViewHolder viewHolder = null;
46 | if (convertView == null) {
47 | convertView = LayoutInflater.from(mCtx).inflate(R.layout.item_sms, null);
48 | viewHolder = new SmsViewHolder();
49 | viewHolder.name = (TextView) convertView.findViewById(R.id.name);
50 | viewHolder.phone = (TextView) convertView.findViewById(R.id.phone);
51 | viewHolder.content = (TextView) convertView.findViewById(R.id.content);
52 | convertView.setTag(viewHolder);
53 | } else {
54 | viewHolder = (SmsViewHolder) convertView.getTag();
55 | }
56 | SmsBean item = getItem(position);
57 | viewHolder.phone.setText(item.getPhone());
58 | viewHolder.name.setText(item.getName());
59 | viewHolder.content.setText(item.getContent());
60 | return convertView;
61 | }
62 |
63 | private class SmsViewHolder {
64 | TextView name;
65 | TextView content;
66 | TextView phone;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/adapter/decoration/ContactDecoration.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.adapter.decoration;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 | import android.graphics.Rect;
6 | import android.graphics.RectF;
7 |
8 | import android.view.View;
9 |
10 | import androidx.recyclerview.widget.RecyclerView;
11 |
12 | /**
13 | * Created by WHF on 2017/3/28.
14 | */
15 |
16 | public class ContactDecoration extends RecyclerView.ItemDecoration {
17 |
18 | private static final int WIDTH = 1;
19 | private static final int LINE_COLOR = 0xFFCCCACA;
20 | public ContactDecoration() {
21 | super();
22 | }
23 |
24 | @Override
25 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
26 | Paint paint = new Paint();
27 | paint.setColor(LINE_COLOR);
28 | int count = parent.getChildCount();
29 | for (int i = 0 ; i smsIntercept = dataBaseManager.getSmsIntercept();
52 |
53 | if (mNativeDataManager.getReceiver()) {
54 | Bundle bundle = intent.getExtras();
55 | if (bundle != null) {
56 | Object[] pdus = (Object[]) bundle.get("pdus");
57 | int subscriptionId = bundle.getInt("subscription", -1);
58 | LogUtils.i(TAG, "当前卡的id: subscriptionId " + subscriptionId); //1是卡1,2是卡2
59 | SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
60 | if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
61 | // TODO: Consider calling
62 | // ActivityCompat#requestPermissions
63 | // here to request the missing permissions, and then overriding
64 | // public void onRequestPermissionsResult(int requestCode, String[] permissions,
65 | // int[] grantResults)
66 | // to handle the case where the user grants the permission. See the documentation
67 | // for ActivityCompat#requestPermissions for more details.
68 | return;
69 | }
70 | SubscriptionInfo subscriptionInfo = subscriptionManager.getActiveSubscriptionInfo(subscriptionId);
71 | if (subscriptionInfo != null) {
72 | // 获取SIM卡的详细信息
73 | CharSequence carrierName = subscriptionInfo.getCarrierName();
74 | String number = subscriptionInfo.getNumber();
75 | LogUtils.i(TAG, "卡的信息: carrierName " + carrierName + " number " + number);
76 | // 使用这些信息来判断是哪个SIM卡
77 | }
78 |
79 | String content = "";
80 | SmsMessage sms = null;
81 | String mobile = "";
82 | //这里把他原来的拼上了.应该不会有其他问题..
83 | for (int i = 0; i < pdus.length; i++) {
84 | sms = SmsMessage.createFromPdu((byte[]) pdus[i]);
85 | content += sms.getMessageBody();
86 | mobile = sms.getOriginatingAddress();
87 |
88 |
89 | }
90 | LogUtils.i("发送人的手机号", "mobile: " + mobile);
91 | for (int i = 0; i < smsIntercept.size(); i++) {
92 | LogUtils.i("MessageReceiver", "smsIntercept.get(" + i + ").getPhone()" + smsIntercept.get(i).getPhone());
93 | if (smsIntercept.get(i).getPhone().equals(mobile)) {
94 | //黑名单短信
95 | LogUtils.i("MessageReceiver", "intercept---->" + mobile);
96 | return;
97 | }
98 | }
99 | if (FormatMobile.hasPrefix(mobile)) {
100 | mobile = FormatMobile.formatMobile(mobile);
101 | }
102 | LogUtils.i(TAG, "sendSms: " + mobile + " -> content " + content + " -> 卡:" + subscriptionId);
103 | //判断是否选择卡1还是卡2发送,
104 | // sendSms(context, mobile, content, mNativeDataManager.getSimIndex());
105 | startSmsService(context, mobile, content, subscriptionId);
106 | }
107 | }
108 | }
109 |
110 | public static ComponentName startSmsService(final Context context, String mobile, String content, int subscriptionId) {
111 |
112 |
113 | // String mobile = sms.getOriginatingAddress();//发送短信的手机号码
114 |
115 | if (FormatMobile.hasPrefix(mobile)) {
116 | mobile = FormatMobile.formatMobile(mobile);
117 | }
118 | // String content = sms.getMessageBody();//短信内容
119 |
120 | Intent serviceIntent = new Intent(context, SmsService.class);
121 | serviceIntent.putExtra(Constant.EXTRA_MESSAGE_CONTENT, content);
122 | serviceIntent.putExtra(Constant.EXTRA_MESSAGE_MOBILE, mobile);
123 | serviceIntent.putExtra(Constant.EXTRA_MESSAGE_RECEIVED_MOBILE_SUBID, subscriptionId);
124 |
125 | return context.startService(serviceIntent);
126 | }
127 |
128 |
129 | // 发送短信的通用方法
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/service/ForegroundService.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.service;
2 |
3 | import android.app.Notification;
4 | import android.app.NotificationChannel;
5 | import android.app.NotificationManager;
6 | import android.app.Service;
7 | import android.content.Intent;
8 | import android.os.Build;
9 | import android.os.IBinder;
10 |
11 | import androidx.annotation.Nullable;
12 | import androidx.core.app.NotificationCompat;
13 |
14 | import com.hosea.messagerelayer.R;
15 |
16 | public class ForegroundService extends Service {
17 |
18 | private static final String CHANNEL_ID = "ForegroundServiceChannel";
19 |
20 | @Override
21 | public void onCreate() {
22 | super.onCreate();
23 | }
24 |
25 | @Override
26 | public int onStartCommand(Intent intent, int flags, int startId) {
27 | createNotificationChannel();
28 | Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
29 | .setContentTitle("前台服务通知")
30 | .setContentText("这是一个保持应用活跃的前台服务,我活着才有希望。")
31 | .setSmallIcon(R.mipmap.icon)
32 | // 如果有,可以设置点击通知后的动作
33 | //.setContentIntent(pendingIntent)
34 | .build();
35 |
36 | startForeground(1, notification);
37 |
38 | // 如果你希望服务在被杀死后有尝试重新启动的行为,可以返回 START_STICKY
39 | return START_NOT_STICKY;
40 | }
41 |
42 | @Override
43 | public void onDestroy() {
44 | super.onDestroy();
45 | }
46 |
47 | @Nullable
48 | @Override
49 | public IBinder onBind(Intent intent) {
50 | return null;
51 | }
52 |
53 | private void createNotificationChannel() {
54 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
55 | NotificationChannel serviceChannel = new NotificationChannel(
56 | CHANNEL_ID,
57 | "前台服务通道",
58 | NotificationManager.IMPORTANCE_DEFAULT
59 | );
60 |
61 | NotificationManager manager = getSystemService(NotificationManager.class);
62 | manager.createNotificationChannel(serviceChannel);
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/service/SmsService.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.service;
2 |
3 | import android.Manifest;
4 | import android.app.IntentService;
5 | import android.content.Intent;
6 | import android.content.pm.PackageManager;
7 | import android.telephony.SubscriptionInfo;
8 | import android.telephony.SubscriptionManager;
9 | import android.widget.Toast;
10 |
11 | import androidx.core.app.ActivityCompat;
12 |
13 | import com.blankj.utilcode.util.LogUtils;
14 | import com.hosea.messagerelayer.bean.Contact;
15 | import com.hosea.messagerelayer.confing.Constant;
16 | import com.hosea.messagerelayer.utils.ContactManager;
17 | import com.hosea.messagerelayer.utils.EmailRelayerManager;
18 | import com.hosea.messagerelayer.utils.NativeDataManager;
19 | import com.hosea.messagerelayer.utils.SmsRelayerManager;
20 | import com.hosea.messagerelayer.utils.db.DataBaseManager;
21 |
22 | import java.util.ArrayList;
23 | import java.util.Set;
24 | import java.util.regex.Matcher;
25 | import java.util.regex.Pattern;
26 |
27 | public class SmsService extends IntentService {
28 |
29 | private NativeDataManager mNativeDataManager;
30 | private DataBaseManager mDataBaseManager;
31 |
32 | public SmsService() {
33 | super("SmsService");
34 | }
35 |
36 | public SmsService(String name) {
37 | super(name);
38 | }
39 |
40 | private int subId = 0;
41 |
42 | @Override
43 | protected void onHandleIntent(Intent intent) {
44 | mNativeDataManager = new NativeDataManager(this);
45 | mDataBaseManager = new DataBaseManager(this);
46 |
47 | String mobile = intent.getStringExtra(Constant.EXTRA_MESSAGE_MOBILE);
48 | String content = intent.getStringExtra(Constant.EXTRA_MESSAGE_CONTENT);
49 | subId = intent.getIntExtra(Constant.EXTRA_MESSAGE_RECEIVED_MOBILE_SUBID, -1);
50 | Set keySet = mNativeDataManager.getKeywordSet();
51 | ArrayList contactList = mDataBaseManager.getAllContact();
52 | //无转发规则
53 | if (keySet.size() == 0 && contactList.size() == 0) {
54 | relayMessage(content, mobile);
55 | } else if (keySet.size() != 0 && contactList.size() == 0) {//仅有关键字规则
56 | for (String key : keySet) {
57 | if (content.contains(key)) {
58 | relayMessage(content, mobile);
59 | break;
60 | }
61 | }
62 | } else if (keySet.size() == 0 && contactList.size() != 0) {//仅有手机号规则
63 | for (Contact contact : contactList) {
64 | if (contact.getContactNum().equals(mobile)) {
65 | relayMessage(content, mobile);
66 | break;
67 | }
68 | }
69 | } else {//两种规则共存
70 | out:
71 | for (Contact contact : contactList) {
72 | if (contact.getContactNum().equals(mobile)) {
73 | for (String key : keySet) {
74 | if (content.contains(key)) {
75 | relayMessage(content, mobile);
76 | break out;
77 | }
78 | }
79 | }
80 | }
81 | }
82 | }
83 |
84 | private void relayMessage(final String content, final String mobile) {
85 | new Thread(new Runnable() {
86 | @Override
87 | public void run() {
88 | SubscriptionManager subscriptionManager = SubscriptionManager.from(SmsService.this);
89 | if (ActivityCompat.checkSelfPermission(SmsService.this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
90 | // TODO: Consider calling
91 | // ActivityCompat#requestPermissions
92 | // here to request the missing permissions, and then overriding
93 | // public void onRequestPermissionsResult(int requestCode, String[] permissions,
94 | // int[] grantResults)
95 | // to handle the case where the user grants the permission. See the documentation
96 | // for ActivityCompat#requestPermissions for more details.
97 | return;
98 | }
99 | SubscriptionInfo subscriptionInfo = subscriptionManager.getActiveSubscriptionInfo(subId);
100 | String receivedMobile = "";
101 | if (subscriptionInfo != null) {
102 | // 获取SIM卡的详细信息
103 | CharSequence carrierName = subscriptionInfo.getCarrierName();
104 | receivedMobile = subscriptionInfo.getNumber();
105 | LogUtils.i(" carrierName " + carrierName + " number " + receivedMobile);
106 | // 使用这些信息来判断是哪个SIM卡
107 | if (receivedMobile.isEmpty()) {
108 | Toast.makeText(SmsService.this, "无法获取到手机号,请确实手机信息权限", Toast.LENGTH_LONG).show();
109 | }
110 | }
111 |
112 |
113 | String dContent = content;
114 | String suffix = mNativeDataManager.getContentSuffix();
115 | String prefix = mNativeDataManager.getContentPrefix();
116 | if (suffix != null) {
117 | dContent = dContent + suffix;
118 | }
119 | if (prefix != null) {
120 | dContent = prefix + dContent;
121 | }
122 | ArrayList mContactList = ContactManager.getContactList(SmsService.this);
123 | for (int i = 0; i < mContactList.size(); i++) {
124 | if (mContactList.get(i).getContactNum().equals(mobile)) {
125 | LogUtils.i("找到了备注:" + mContactList.get(i).getContactNum());
126 | LogUtils.i("找到了备注:" + mContactList.get(i).getContactName());
127 | dContent = "联系人: " + mContactList.get(i).getContactName() + "\n" +
128 | "发送号码: " + mContactList.get(i).getContactNum() + "\n" + dContent;
129 | }
130 | }
131 | String extractCode = extractCode(dContent);
132 | if (!dContent.contains("联系人:")) {
133 | dContent = "发送号码: " + mobile + "\n" + dContent;
134 | //+86XXXXXXXXXXX
135 | // dContent = receivedMobile.substring(10) + "->" + mobile + "\n" + dContent;
136 |
137 | }
138 | LogUtils.i("最终转发出的内容:" + dContent);
139 | // if (mNativeDataManager.getSmsRelay()) {
140 | // SmsRelayerManager.relaySms(SmsService.this, mNativeDataManager.getObjectMobile(), dContent, mNativeDataManager.getSimIndex());
141 | // }
142 | if (mNativeDataManager.getEmailRelay()) {
143 | dContent = dContent.replace("\n", "
");
144 | LogUtils.i("\n换成br =>" + dContent);
145 | String title = "";
146 |
147 | if (extractCode == null) {
148 | title = "尾号:" + receivedMobile.substring(10);
149 | } else {
150 | title = "尾号:" + receivedMobile.substring(10) + "->" + "验证码:" + extractCode;
151 | }
152 | LogUtils.i("准备发送邮件:", title, dContent);
153 | EmailRelayerManager.relayEmail(mNativeDataManager, title, dContent);
154 | }
155 | LogUtils.i("mobile=>" + mobile);
156 | if (mNativeDataManager.getInnerRelay() && mobile.equals(mNativeDataManager.getInnerMobile())) {
157 |
158 | int sIndex = content.indexOf(mNativeDataManager.getInnerRule());
159 | if (sIndex != -1) {
160 | LogUtils.i("转发内部短信");
161 | String transferPhone = content.substring(0, sIndex);
162 | String transferContent = content.substring(sIndex + 1);
163 | //这里simIndex卡1是0卡2是1
164 | SmsRelayerManager.relaySms(SmsService.this, transferPhone, transferContent, subId);
165 | }
166 |
167 | }
168 |
169 | // if (mNativeDataManager.getWeChatRelay()) {
170 | // //这里如果要做好需要自己做一些判断..比如当前显示的是哪个界面.
171 | // //微信就不用管.其他界面就给提示.不能处理或者手动跳转处理.
172 | // //我这边用的测试机一直在聊天界面这里就先不处理这个东西了.
173 | // LogUtils.i("relayMessage: " + dContent);
174 | // WeChatRelayerManager.jumpWeChat(getBaseContext(), dContent);
175 | // }
176 | }
177 | }).start();
178 |
179 |
180 | }
181 |
182 | @Override
183 | public void onDestroy() {
184 | mDataBaseManager.closeHelper();
185 | super.onDestroy();
186 | }
187 |
188 | public static String extractCode(String content) {
189 | // 正则表达式匹配4位数字验证码
190 | if (content.contains("码") || content.contains("code")) {
191 | String regex = "(\\d{4,6})";
192 | Pattern pattern = Pattern.compile(regex);
193 | Matcher matcher = pattern.matcher(content);
194 |
195 | if (matcher.find()) {
196 | // 返回第一个匹配的验证码
197 | return matcher.group();
198 | }
199 | // 如果没有找到匹配的,返回null
200 | return null;
201 | } else {
202 | return null;
203 | }
204 |
205 |
206 | }
207 |
208 | }
209 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/utils/AccessibilityUtil.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.utils;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.provider.Settings;
6 | import android.text.TextUtils;
7 |
8 | import com.hosea.messagerelayer.service.AccessibilitySampleService;
9 |
10 |
11 | /**
12 | * 辅助功能相关检查的帮助类
13 | */
14 | public class AccessibilityUtil {
15 | private static final String ACCESSIBILITY_SERVICE_PATH = AccessibilitySampleService.class.getCanonicalName();
16 | /**
17 | * 判断是否有辅助功能权限
18 | *
19 | * @param context
20 | * @return
21 | */
22 | public static boolean isAccessibilitySettingsOn(Context context) {
23 | if (context == null) {
24 | return false;
25 | }
26 |
27 | int accessibilityEnabled = 0;
28 | try {
29 | accessibilityEnabled = Settings.Secure.getInt(context.getApplicationContext().getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED);
30 | } catch (Settings.SettingNotFoundException e) {
31 | e.printStackTrace();
32 | }
33 |
34 | String packageName = context.getPackageName();
35 | final String serviceStr = packageName + "/" + ACCESSIBILITY_SERVICE_PATH;
36 | if (accessibilityEnabled == 1) {
37 | TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
38 |
39 | String settingValue = Settings.Secure.getString(context.getApplicationContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
40 | if (settingValue != null) {
41 | TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
42 | splitter.setString(settingValue);
43 | while (splitter.hasNext()) {
44 | String accessabilityService = splitter.next();
45 |
46 | if (accessabilityService.equalsIgnoreCase(serviceStr)) {
47 | return true;
48 | }
49 | }
50 | }
51 | }
52 | return false;
53 | }
54 |
55 | public static Intent getAccessibilitySettingPageIntent(Context context) {
56 | // 一些品牌的手机可能不是这个Intent,需要适配
57 | return new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/utils/ContactManager.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.utils;
2 |
3 | import android.content.Context;
4 | import android.database.Cursor;
5 | import android.provider.ContactsContract;
6 |
7 | import com.hosea.messagerelayer.bean.Contact;
8 |
9 | import java.util.ArrayList;
10 |
11 |
12 | /**
13 | * Created by WHF on 2017/3/28.
14 | */
15 |
16 | public class ContactManager {
17 |
18 | /**
19 | * 从ContentProvider中获取所有联系人
20 | * @param context
21 | * @return
22 | */
23 | public static ArrayList getContactList(Context context){
24 | ArrayList mContactList = new ArrayList<>();
25 |
26 | Cursor cursor = context.getContentResolver()
27 | .query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI
28 | ,null,null,null,ContactsContract.CommonDataKinds.Phone.SORT_KEY_PRIMARY);
29 | while (cursor.moveToNext()){
30 | Contact contact = new Contact();
31 | contact.setContactName(cursor.getString(cursor
32 | .getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)));
33 |
34 | contact.setContactNum(cursor.getString(cursor
35 | .getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
36 |
37 | mContactList.add(contact);
38 |
39 | }
40 | return mContactList;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/utils/EmailRelayerManager.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.utils;
2 |
3 | import android.util.Log;
4 |
5 | import com.blankj.utilcode.util.LogUtils;
6 | import com.hosea.messagerelayer.bean.EmailMessage;
7 | import com.hosea.messagerelayer.confing.Constant;
8 |
9 | import java.io.UnsupportedEncodingException;
10 | import java.util.Properties;
11 | import java.util.regex.Matcher;
12 | import java.util.regex.Pattern;
13 |
14 | import javax.mail.Authenticator;
15 | import javax.mail.MessagingException;
16 | import javax.mail.PasswordAuthentication;
17 | import javax.mail.Session;
18 | import javax.mail.Transport;
19 | import javax.mail.internet.InternetAddress;
20 | import javax.mail.internet.MimeMessage;
21 |
22 | /**
23 | * Created by WHF on 2017/3/25.
24 | */
25 |
26 | public class EmailRelayerManager {
27 |
28 | public static final int CODE_SUCCESS = 0x1;
29 | public static final int CODE_FAILE = 0x0;
30 |
31 | private static final String PORT_SSL = "465";
32 |
33 | private static final String HOST_QQ = "smtp.qq.com";
34 | private static final String HOST_163 = "smtp.163.com";
35 | private static final String HOST_126 = "smtp.126.com";
36 | private static final String HOST_GMAIL = "smtp.gmail.com";
37 | private static final String HOST_OUTLOOK = "smtp.outlook.com";
38 |
39 | //发送短信至目标邮件
40 | public static int relayEmail(NativeDataManager dataManager, String title, String content) {
41 | Properties props = new Properties();
42 | User user = getSenderUser(dataManager);
43 | EmailMessage emailMessage = creatEmailMessage(title, content, dataManager);
44 | setHost(dataManager, props);
45 |
46 | //是否开启SSL
47 | if (dataManager.getEmailSsl()) {
48 | if (dataManager.getEmailServicer() == Constant.EMAIL_SERVICER_OTHER) {
49 | setSslMode(props, PORT_SSL);
50 | } else {
51 | String port = dataManager.getEmailPort();
52 | if (port != null) {
53 | setSslMode(props, port);
54 | }
55 | }
56 | }
57 |
58 | setSenderToPro(props, user);
59 | props.put("mail.smtp.auth", true);//如果不设置,则报553错误
60 | props.put("mail.transport.protocol", "smtp");
61 | props.put("mail.smtp.ssl.enable", "true");
62 |
63 |
64 | //getDefaultInstace得到的始终是该方法初次创建的缺省的对象,getInstace每次获取新对象
65 | Session session = Session.getInstance(props
66 | , new SmtpAuthenticator(user));
67 | session.setDebug(true);
68 |
69 | try {
70 | MimeMessage message = creatMessage(session, emailMessage);
71 | Transport.send(message);
72 | LogUtils.i("EmailRelayerManager", "发送成功");
73 | return CODE_SUCCESS;
74 | } catch (MessagingException e) {
75 | LogUtils.e("error:", e.getMessage(),e.getCause());
76 | LogUtils.e("EmailRelayerManager", "发送失败");
77 | e.printStackTrace();
78 | return CODE_FAILE;
79 | } catch (UnsupportedEncodingException e) {
80 | LogUtils.e("error:", e.getMessage(),e.getCause());
81 | LogUtils.e("EmailRelayerManager", "发送失败");
82 | e.printStackTrace();
83 | return CODE_FAILE;
84 | }
85 | }
86 |
87 | /**
88 | * 创建邮件消息对象
89 | *
90 | * @param session
91 | * @param emailMessage
92 | * @return
93 | * @throws UnsupportedEncodingException
94 | * @throws MessagingException
95 | */
96 | private static MimeMessage creatMessage(Session session, EmailMessage emailMessage)
97 | throws UnsupportedEncodingException, MessagingException {
98 |
99 | MimeMessage message = new MimeMessage(session);
100 | message.setFrom(new InternetAddress(emailMessage.getSenderAccount()
101 | , emailMessage.getSenderName(), "UTF-8"));//发件人
102 | message.setRecipients(MimeMessage.RecipientType.TO, emailMessage.getReceiverAccount());//收件人
103 | message.setSubject(emailMessage.getSubject());//主题
104 | message.setContent(emailMessage.getContent(), "text/html;charset=UTF-8");
105 | return message;
106 | }
107 |
108 | /**
109 | * SMTP 服务器的端口 (非 SSL 连接的端口一般默认为 25, 可以不添加, 如果
110 | * 开启了 SSL 连接,需要改为对应邮箱的 SMTP 服务器的端口, 具体可查看对应
111 | * 邮箱服务的帮助,QQ邮箱的SMTP(SLL)端口为465或587, 其他邮箱自行去查看)
112 | *
113 | * @param props
114 | * @param smtpPort
115 | * @return
116 | */
117 | private static void setSslMode(Properties props, String smtpPort) {
118 | props.setProperty("mail.smtp.port", smtpPort);
119 | props.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
120 | props.setProperty("mail.smtp.socketFactory.fallback", "false");
121 | props.setProperty("mail.smtp.socketFactory.port", smtpPort);
122 | }
123 |
124 | /**
125 | * 从本地数据获取发送方账号和密码
126 | *
127 | * @param dataManager
128 | * @return
129 | */
130 | private static User getSenderUser(NativeDataManager dataManager) {
131 | return new User(dataManager.getEmailAccount(), dataManager.getEmailPassword());
132 | }
133 |
134 | /**
135 | * 将发送发的账号和密码设置给配置文件
136 | *
137 | * @param properties
138 | * @param user
139 | * @return
140 | */
141 | private static void setSenderToPro(Properties properties, User user) {
142 | properties.put("mail.smtp.username", user.account);
143 | properties.put("mail.smtp.password", user.password);
144 | }
145 |
146 | /**
147 | * 设置主机
148 | *
149 | * @param dataManager
150 | * @param props
151 | * @return
152 | */
153 | private static void setHost(NativeDataManager dataManager, Properties props) {
154 |
155 | switch (dataManager.getEmailServicer()) {
156 | case Constant.EMAIL_SERVICER_QQ:
157 | props.put("mail.smtp.host", HOST_QQ);
158 | break;
159 | case Constant.EMAIL_SERVICER_163:
160 | props.put("mail.smtp.host", HOST_163);
161 | break;
162 | case Constant.EMAIL_SERVICER_126:
163 | props.put("mail.smtp.host", HOST_126);
164 | break;
165 | case Constant.EMAIL_SERVICER_OUTLOOK:
166 | props.put("mail.smtp.host", HOST_OUTLOOK);
167 | break;
168 | case Constant.EMAIL_SERVICER_GMAIL:
169 | props.put("mail.smtp.host", HOST_GMAIL);
170 | break;
171 | case Constant.EMAIL_SERVICER_OTHER:
172 | String host = dataManager.getEmailHost();
173 | if (host != null) {
174 | props.put("mail.smtp.host", host);
175 | }
176 | break;
177 | }
178 | }
179 |
180 |
181 | /**
182 | * 登录认证
183 | */
184 | private static class SmtpAuthenticator extends Authenticator {
185 | String mUsername;
186 | String mPassword;
187 |
188 | public SmtpAuthenticator(User user) {
189 | super();
190 | this.mUsername = user.account;
191 | this.mPassword = user.password;
192 | }
193 |
194 | @Override
195 | public PasswordAuthentication getPasswordAuthentication() {
196 | if ((mUsername != null) && (mUsername.length() > 0) && (mPassword != null)
197 | && (mPassword.length() > 0)) {
198 | return new PasswordAuthentication(mUsername, mPassword);
199 | }
200 | return null;
201 | }
202 | }
203 |
204 | /**
205 | * 发送方账户密码实体类
206 | */
207 | private static class User {
208 | String account;
209 | String password;
210 |
211 | User(String account, String password) {
212 | this.account = account;
213 | this.password = password;
214 | }
215 | }
216 |
217 | /**
218 | * 封装消息实体
219 | *
220 | * @param content
221 | * @param dataManager
222 | * @return
223 | */
224 | private static EmailMessage creatEmailMessage(String title, String content, NativeDataManager dataManager) {
225 | EmailMessage message = new EmailMessage();
226 | message.setContent(content);
227 | message.setSenderAccount(dataManager.getEmailAccount());
228 | message.setSenderName(dataManager.getEmailSenderName());
229 | message.setReceiverAccount(dataManager.getEmailToAccount());
230 | LogUtils.i("EmailRelayerManager", "real_length()" + getRealLength(content) + "");
231 | LogUtils.i("EmailRelayerManager", "content.length():" + content.length() + "");
232 | //小于中文加英文大于240个字符就显示原主题.
233 | if (getRealLength(content) < 240) {
234 | String[] split = content.split("
");
235 | if (split.length > 2) {
236 | message.setSubject(split[1].trim());
237 | } else {
238 | //发送测试配置.
239 | message.setSubject(title);
240 | }
241 | } else {
242 | // message.setSubject(dataManager.getEmailSubject());
243 | message.setSubject(title);
244 | }
245 | return message;
246 | }
247 |
248 |
249 | public static int getRealLength(String str) {
250 | int m = 0;
251 | char arr[] = str.toCharArray();
252 | for (int i = 0; i < arr.length; i++) {
253 | char c = arr[i];
254 | // 中文字符(根据Unicode范围判断),中文字符长度为2
255 | if ((c >= 0x0391 && c <= 0xFFE5)) {
256 | m = m + 2;
257 | } else if ((c >= 0x0000 && c <= 0x00FF)) // 英文字符
258 | {
259 | m = m + 1;
260 | }
261 | }
262 | return m;
263 |
264 | }
265 |
266 | }
267 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/utils/FormatMobile.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.utils;
2 |
3 | /**
4 | * Created by WHF on 2017/3/30.
5 | */
6 |
7 | public class FormatMobile {
8 |
9 | public static Boolean hasPrefix(String mobile){
10 | return mobile.startsWith("+86");
11 | }
12 |
13 | public static String formatMobile(String mobile){
14 | return mobile.substring(3);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/utils/NativeDataManager.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.utils;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 | import com.hosea.messagerelayer.confing.Constant;
7 |
8 | import java.util.HashSet;
9 | import java.util.Set;
10 |
11 | /**
12 | * Created by WHF on 2017/3/24.
13 | */
14 |
15 | public class NativeDataManager {
16 |
17 | private SharedPreferences mPreference;
18 |
19 | public NativeDataManager(Context context) {
20 | mPreference = context.getSharedPreferences(Constant.SETTING_FILE_NAME, Context.MODE_PRIVATE);
21 | }
22 |
23 | public boolean getSendSMSHint() {
24 | return mPreference.getBoolean(Constant.SEND_PERMISSION_HINT, true);
25 | }
26 |
27 | public void setSendSMSHint(boolean hint) {
28 | mPreference.edit().putBoolean(Constant.SEND_PERMISSION_HINT, hint).apply();
29 | }
30 |
31 | public String getObjectMobile() {
32 | return mPreference.getString(Constant.KEY_OBJECT_MOBILE, "点击设置");
33 | }
34 | public String getInnerMobile() {
35 | return mPreference.getString(Constant.KEY_INNER_MOBILE, "点击设置");
36 | }
37 |
38 | public void setObjectMobile(String mobile) {
39 | mPreference.edit().putString(Constant.KEY_OBJECT_MOBILE, mobile).apply();
40 | }
41 | public void setInnerMobile(String mobile) {
42 | mPreference.edit().putString(Constant.KEY_INNER_MOBILE, mobile).apply();
43 | }
44 |
45 | public void setSmsRelay(Boolean b) {
46 | mPreference.edit().putBoolean(Constant.KEY_RELAY_SMS, b).apply();
47 | }
48 |
49 | public Boolean getSmsRelay() {
50 | return mPreference.getBoolean(Constant.KEY_RELAY_SMS, false);
51 | }
52 |
53 | public void setInnerRelay(Boolean b) {
54 | mPreference.edit().putBoolean(Constant.KEY_RELAY_INNER_SMS, b).apply();
55 | }
56 |
57 | public Boolean getInnerRelay() {
58 | return mPreference.getBoolean(Constant.KEY_RELAY_INNER_SMS, false);
59 | }
60 | public void setInnerRule(String b) {
61 | mPreference.edit().putString(Constant.KEY_INNER_MOBILE_RULE, b).apply();
62 | }
63 |
64 | public String getInnerRule() {
65 | return mPreference.getString(Constant.KEY_INNER_MOBILE_RULE, "点击设置");
66 | }
67 |
68 | public void setEmailRelay(Boolean b) {
69 | mPreference.edit().putBoolean(Constant.KEY_RELAY_EMAIL, b).apply();
70 | }
71 |
72 | public boolean getEmailRelay() {
73 | return mPreference.getBoolean(Constant.KEY_RELAY_EMAIL, false);
74 | }
75 |
76 | public boolean getWeChatRelay() {
77 | return mPreference.getBoolean(Constant.KEY_RELAY_WECHAT, false);
78 | }
79 |
80 | public String getWeChatName() {
81 | return mPreference.getString(Constant.WECHAT_NAME, "");
82 | }
83 |
84 | public void setWeChatName(String name) {
85 | mPreference.edit().putString(Constant.WECHAT_NAME, name).apply();
86 | }
87 |
88 | public String getWeChatContent() {
89 | return mPreference.getString(Constant.WECHAT_CONTENT, "");
90 | }
91 |
92 | public void setWeChatContent(String name) {
93 | mPreference.edit().putString(Constant.WECHAT_CONTENT, name).apply();
94 | }
95 |
96 | public void setWeChatRelay(Boolean b) {
97 | mPreference.edit().putBoolean(Constant.KEY_RELAY_WECHAT, b).apply();
98 | }
99 |
100 | public void setReceiver(Boolean b) {
101 | mPreference.edit().putBoolean(Constant.KEY_RECEIVER, b).apply();
102 | }
103 |
104 | public boolean getReceiver() {
105 | return mPreference.getBoolean(Constant.KEY_RECEIVER, true);
106 | }
107 |
108 | public String getEmailServicer() {
109 | return mPreference.getString(Constant.KEY_EMAIL_SERVICER, Constant.EMAIL_SERVICER_QQ);
110 | }
111 |
112 | public void setEmailServicer(String servicer) {
113 | mPreference.edit().putString(Constant.KEY_EMAIL_SERVICER, servicer).apply();
114 | }
115 |
116 | public void setEmailAccount(String account) {
117 | mPreference.edit().putString(Constant.KEY_EMAIL_ACCOUNT, account).apply();
118 | }
119 |
120 | public String getEmailAccount() {
121 | return mPreference.getString(Constant.KEY_EMAIL_ACCOUNT, "点击设置");
122 | }
123 |
124 | public void setEmailPassword(String password) {
125 | mPreference.edit().putString(Constant.KEY_EMAIL_PASSWORD, password).apply();
126 | }
127 |
128 | public String getEmailPassword() {
129 | return mPreference.getString(Constant.KEY_EMAIL_PASSWORD, null);
130 | }
131 |
132 | public void setEmailHost(String address) {
133 | mPreference.edit().putString(Constant.KEY_EMAIL_HOST, address).apply();
134 | }
135 |
136 | public void setEmailPort(String port) {
137 | mPreference.edit().putString(Constant.KEY_EMAIL_PORT, port).apply();
138 | }
139 |
140 | public String getEmailHost() {
141 | return mPreference.getString(Constant.KEY_EMAIL_HOST, null);
142 | }
143 |
144 | public String getEmailPort() {
145 | return mPreference.getString(Constant.KEY_EMAIL_PORT, null);
146 | }
147 |
148 | public void setEmailSsl(Boolean b) {
149 | mPreference.edit().putBoolean(Constant.KEY_EMAIL_SSL, b).apply();
150 | }
151 |
152 | public Boolean getEmailSsl() {
153 | return mPreference.getBoolean(Constant.KEY_EMAIL_SSL, true);
154 | }
155 |
156 | public void setEmailToAccount(String account) {
157 | mPreference.edit().putString(Constant.KEY_EMAIL_TO_ACCOUNT, account).apply();
158 | }
159 |
160 | public String getEmailToAccount() {
161 | return mPreference.getString(Constant.KEY_EMAIL_TO_ACCOUNT, "点击设置");
162 | }
163 |
164 | public void setEmailSenderName(String name) {
165 | mPreference.edit().putString(Constant.KEY_EMAIL_SENDER_NAME, name).apply();
166 | }
167 |
168 | public String getEmailSenderName() {
169 | return mPreference.getString(Constant.KEY_EMAIL_SENDER_NAME, "短信助手");
170 | }
171 |
172 | public void setEmailSubject(String subject) {
173 | mPreference.edit().putString(Constant.KEY_EMAIL_SUBJECT, subject).apply();
174 | }
175 |
176 | public String getEmailSubject() {
177 | return mPreference.getString(Constant.KEY_EMAIL_SUBJECT, "短信转发");
178 | }
179 |
180 | public void setKeywordSet(Set values) {
181 | mPreference.edit().putStringSet(Constant.KEY_KEYWORD_LIST, values).apply();
182 | }
183 |
184 | public Set getKeywordSet() {
185 | return mPreference.getStringSet(Constant.KEY_KEYWORD_LIST, new HashSet());
186 | }
187 |
188 | public void setContentPrefix(String prefix) {
189 | mPreference.edit().putString(Constant.KEY_CONTENT_PREFIX, prefix).apply();
190 | }
191 |
192 | public void setContentSuffix(String suffix) {
193 | mPreference.edit().putString(Constant.KEY_CONTENT_SUFFIX, suffix).apply();
194 | }
195 |
196 | public String getContentSuffix() {
197 | return mPreference.getString(Constant.KEY_CONTENT_SUFFIX, null);
198 | }
199 |
200 | public String getContentPrefix() {
201 | return mPreference.getString(Constant.KEY_CONTENT_PREFIX, null);
202 | }
203 |
204 |
205 | // /**
206 | // * 用户选择的是卡1还是卡2
207 | // */
208 | // public int getSimIndex(){
209 | // return mPreference.getInt(Constant.SIM_INDEX, 0);
210 | // }
211 |
212 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/utils/OpenAccessibilitySettingHelper.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.utils;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 |
6 | import com.hosea.messagerelayer.activity.AccessibilityOpenHelperActivity;
7 |
8 | public class OpenAccessibilitySettingHelper {
9 | private static final String ACTION = "action";
10 | private static final String ACTION_START_ACCESSIBILITY_SETTING = "action_start_accessibility_setting";
11 |
12 | public static void jumpToSettingPage(Context context) {
13 | try {
14 | Intent intent = new Intent(context, AccessibilityOpenHelperActivity.class);
15 | intent.putExtra(ACTION, ACTION_START_ACCESSIBILITY_SETTING);
16 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
17 | context.startActivity(intent);
18 | } catch (Exception ignore) {}
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/utils/SmsRelayerManager.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.utils;
2 |
3 | import android.Manifest;
4 | import android.app.PendingIntent;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.pm.PackageManager;
8 | import android.os.Bundle;
9 | import android.telephony.SmsManager;
10 | import android.telephony.SubscriptionInfo;
11 | import android.telephony.SubscriptionManager;
12 |
13 | import androidx.core.app.ActivityCompat;
14 |
15 | import com.blankj.utilcode.util.LogUtils;
16 | import com.hosea.messagerelayer.activity.MainActivity;
17 |
18 | import java.util.ArrayList;
19 | import java.util.List;
20 |
21 | /**
22 | * Created by WHF on 2017/3/26.
23 | */
24 |
25 | public class SmsRelayerManager {
26 | // /**
27 | // * 发送短信至目标手机号
28 | // * @param dataManager
29 | // * @param content 短信内容
30 | // */
31 | // public static void relaySms(Context context, NativeDataManager dataManager, String content) {
32 | // String objectMobile = dataManager.getObjectMobile();
33 | // android.telephony.SmsManager smsManager = android.telephony.SmsManager.getDefault();
34 | // if (content.length()>70) {//短信内容大于70字数
35 | // ArrayList divideContents =smsManager.divideMessage(content);//将短信切分成集合
36 | // smsManager.sendMultipartTextMessage(objectMobile, null, divideContents, null, null);
37 | // }else{
38 | // smsManager.sendTextMessage(objectMobile, null, content, null, null);
39 | // }
40 | // }
41 |
42 | /**
43 | * 发送短信至目标手机号
44 | *
45 | * @param dataManager
46 | * @param content 短信内容
47 | */
48 | public static void relaySms(Context context, String mobile, String content, int subId) {
49 |
50 | // android.telephony.SmsManager smsManager = android.telephony.SmsManager.getDefault();
51 | SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
52 | if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
53 | // TODO: Consider calling
54 | // ActivityCompat#requestPermissions
55 | // here to request the missing permissions, and then overriding
56 | // public void onRequestPermissionsResult(int requestCode, String[] permissions,
57 | // int[] grantResults)
58 | // to handle the case where the user grants the permission. See the documentation
59 | // for ActivityCompat#requestPermissions for more details.
60 | return;
61 | }
62 | List subscriptionInfoList = subscriptionManager.getActiveSubscriptionInfoList();
63 | for (SubscriptionInfo subscriptionInfo : subscriptionInfoList) {
64 | if (subscriptionInfo.getSubscriptionId() == subId) {
65 | int subscriptionId = subscriptionInfo.getSubscriptionId();
66 | SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subscriptionId);
67 | if (content.length() > 70) {//短信内容大于70字数
68 | ArrayList divideContents = smsManager.divideMessage(content);//将短信切分成集合
69 | LogUtils.i("转发内部短信成功",mobile,divideContents);
70 | smsManager.sendMultipartTextMessage(mobile, null, divideContents, null, null);
71 | } else {
72 | LogUtils.i("转发内部短信成功",mobile,content);
73 | smsManager.sendTextMessage(mobile, null, content, null, null);
74 | }
75 |
76 | break;
77 | }
78 |
79 | }
80 |
81 |
82 | }
83 |
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/utils/WeChatRelayerManager.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.utils;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.content.pm.PackageManager;
6 |
7 | import com.hosea.messagerelayer.confing.Constant;
8 | import com.hosea.messagerelayer.service.AccessibilitySampleService;
9 |
10 | /**
11 | * Created by heliu on 2018/6/29.
12 | */
13 |
14 | public class WeChatRelayerManager {
15 | /**
16 | * 发信到微信.
17 | *
18 | * @param dataManager
19 | * @param content 短信内容
20 | */
21 |
22 |
23 | public static void jumpWeChat(Context context, String content) {
24 | PackageManager packageManager = context.getPackageManager();
25 | Intent it = packageManager.getLaunchIntentForPackage("com.tencent.mm");
26 | context.startActivity(it);
27 | Intent intent = new Intent();
28 | intent.setAction(AccessibilitySampleService.WECHAT_ACTION);
29 | intent.putExtra(Constant.WECHAT_CONTENT, content);
30 | context.sendBroadcast(intent);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/utils/db/DataBaseHelper.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.utils.db;
2 |
3 | import android.content.Context;
4 | import android.database.sqlite.SQLiteDatabase;
5 | import android.database.sqlite.SQLiteOpenHelper;
6 |
7 | import com.hosea.messagerelayer.confing.Constant;
8 | import com.hosea.messagerelayer.confing.SMSConfig;
9 |
10 |
11 | /**
12 | * Created by WHF on 2017/3/28.
13 | */
14 |
15 | public class DataBaseHelper extends SQLiteOpenHelper {
16 |
17 | private static final int VERSION = 1;
18 | private static final String DB_NAME = "contact.db";
19 | private static final String CREAD_DB_SQL = "CREATE TABLE " + Constant.DB_TABLE_NAME +
20 | "(" + Constant.DB_KEY_ID + " integer primary key autoincrement" +
21 | "," + Constant.DB_KEY_NAME + " varchar(20)," + Constant.DB_KEY_MOBLIE + " varchar(20))";
22 |
23 | private static final String CREAD_DB_SQL_SMS = "CREATE TABLE " + SMSConfig.DB_TABLE_NAME +
24 | "(" + SMSConfig.DB_KEY_ID + " integer primary key autoincrement" +
25 | "," + SMSConfig.DB_KEY_NAME + " varchar(20)," + SMSConfig.DB_KEY_MOBLIE + " varchar(20))";
26 |
27 |
28 | public DataBaseHelper(Context context) {
29 | super(context, DB_NAME, null, VERSION);
30 | }
31 |
32 | @Override
33 | public void onCreate(SQLiteDatabase db) {
34 | db.execSQL(CREAD_DB_SQL);
35 | db.execSQL(CREAD_DB_SQL_SMS);
36 | }
37 |
38 | @Override
39 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
40 |
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/utils/db/DataBaseManager.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer.utils.db;
2 |
3 | import android.content.ContentValues;
4 | import android.content.Context;
5 | import android.database.Cursor;
6 | import android.database.sqlite.SQLiteDatabase;
7 |
8 | import com.hosea.messagerelayer.bean.Contact;
9 | import com.hosea.messagerelayer.bean.SmsBean;
10 | import com.hosea.messagerelayer.confing.Constant;
11 | import com.hosea.messagerelayer.confing.SMSConfig;
12 | import com.hosea.messagerelayer.utils.FormatMobile;
13 |
14 | import java.util.ArrayList;
15 | import java.util.List;
16 |
17 |
18 | /**
19 | * 存储被选中的联系人的数据库管理类
20 | * Created by WHF on 2017/3/28.
21 | */
22 |
23 | public class DataBaseManager {
24 |
25 | private DataBaseHelper mHelper;
26 |
27 | public DataBaseManager(Context context) {
28 | this.mHelper = new DataBaseHelper(context);
29 |
30 | }
31 |
32 | /**
33 | * 添加一条数据
34 | *
35 | * @param contact
36 | */
37 | public void addContact(Contact contact) {
38 | SQLiteDatabase database = mHelper.getWritableDatabase();
39 | ContentValues values = new ContentValues();
40 | values.put(Constant.DB_KEY_NAME, contact.getContactName());
41 | values.put(Constant.DB_KEY_MOBLIE, contact.getContactNum());
42 | database.insert(Constant.DB_TABLE_NAME, null, values);
43 | }
44 |
45 |
46 | /**
47 | * 添加一条数据
48 | *
49 | * @param contact
50 | */
51 | public void addSMSIntercept(SmsBean smsBean) {
52 | SQLiteDatabase database = mHelper.getWritableDatabase();
53 | ContentValues values = new ContentValues();
54 | values.put(SMSConfig.DB_KEY_NAME, smsBean.getName());
55 | values.put(SMSConfig.DB_KEY_MOBLIE, smsBean.getPhone());
56 | database.insert(SMSConfig.DB_TABLE_NAME, null, values);
57 | }
58 |
59 |
60 | /**
61 | * 添加多条数据
62 | */
63 | public void addContactList(List contactList) {
64 | SQLiteDatabase database = mHelper.getWritableDatabase();
65 | for (Contact contact : contactList) {
66 | String num = contact.getContactNum();
67 | if (FormatMobile.hasPrefix(num)) {
68 | num = FormatMobile.formatMobile(num);
69 | }
70 | ContentValues values = new ContentValues();
71 | values.put(Constant.DB_KEY_NAME, contact.getContactName());
72 | values.put(Constant.DB_KEY_MOBLIE, num);
73 | database.insert(Constant.DB_TABLE_NAME, null, values);
74 | }
75 | }
76 |
77 | /**
78 | * 获取所有联系人
79 | */
80 | public ArrayList getAllContact() {
81 | ArrayList contactList = new ArrayList<>();
82 | SQLiteDatabase database = mHelper.getReadableDatabase();
83 | Cursor cursor = database.query(Constant.DB_TABLE_NAME
84 | , null, null, null, null, null, null);
85 | while (cursor.moveToNext()) {
86 | Contact contact = new Contact();
87 | contact.setContactName(cursor.getString(cursor.getColumnIndex(Constant.DB_KEY_NAME)));
88 | contact.setContactNum(cursor.getString(cursor.getColumnIndex(Constant.DB_KEY_MOBLIE)));
89 | contactList.add(contact);
90 | }
91 | return contactList;
92 | }
93 |
94 |
95 | /**
96 | * 获取所有联系人
97 | */
98 | public ArrayList getSmsIntercept() {
99 | ArrayList smsBeanArrayList = new ArrayList<>();
100 | SQLiteDatabase database = mHelper.getReadableDatabase();
101 | Cursor cursor = database.query(SMSConfig.DB_TABLE_NAME
102 | , null, null, null, null, null, null);
103 | while (cursor.moveToNext()) {
104 | SmsBean smsBean = new SmsBean();
105 | smsBean.setName(cursor.getString(cursor.getColumnIndex(Constant.DB_KEY_NAME)));
106 | smsBean.setPhone(cursor.getString(cursor.getColumnIndex(Constant.DB_KEY_MOBLIE)));
107 | smsBeanArrayList.add(smsBean);
108 | }
109 | return smsBeanArrayList;
110 | }
111 |
112 | /**
113 | * 删除某一联系人,根据其手机号
114 | *
115 | * @param mobile
116 | */
117 | public void deleteContactFromMobile(String mobile) {
118 | SQLiteDatabase database = mHelper.getWritableDatabase();
119 | database.delete(Constant.DB_TABLE_NAME, Constant.DB_KEY_MOBLIE + "= ?", new String[]{mobile});
120 | }
121 |
122 | /**
123 | * 删除拦截,根据其手机号
124 | *
125 | * @param mobile
126 | */
127 | public void deleteSmsFromMobile(String mobile) {
128 | SQLiteDatabase database = mHelper.getWritableDatabase();
129 | database.delete(SMSConfig.DB_TABLE_NAME, SMSConfig.DB_KEY_MOBLIE + "= ?", new String[]{mobile});
130 | }
131 |
132 |
133 | /**
134 | * 删除所有联系人
135 | */
136 | public void deleteAll() {
137 | SQLiteDatabase database = mHelper.getWritableDatabase();
138 | database.delete(Constant.DB_TABLE_NAME, null, null);
139 | }
140 |
141 | /**
142 | * 关闭SqLiteDatabase
143 | */
144 | public void closeHelper() {
145 | mHelper.close();
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/utils/permission/DefaultRationale.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © Yan Zhenjie
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hosea.messagerelayer.utils.permission;
17 |
18 | import android.content.Context;
19 | import android.content.DialogInterface;
20 |
21 | import android.text.TextUtils;
22 |
23 |
24 | import androidx.appcompat.app.AlertDialog;
25 |
26 | import com.hosea.messagerelayer.R;
27 | import com.yanzhenjie.permission.Permission;
28 | import com.yanzhenjie.permission.Rationale;
29 | import com.yanzhenjie.permission.RequestExecutor;
30 |
31 | import java.util.List;
32 |
33 | /**
34 | * Created by YanZhenjie on 2018/1/1.
35 | */
36 | public final class DefaultRationale implements Rationale {
37 |
38 | @Override
39 | public void showRationale(Context context, List permissions, final RequestExecutor executor) {
40 | List permissionNames = Permission.transformText(context, permissions);
41 | String message = context.getString(R.string.message_permission_rationale, TextUtils.join("\n", permissionNames));
42 |
43 | new AlertDialog.Builder(context)
44 | .setCancelable(false)
45 | .setTitle(R.string.title_dialog)
46 | .setMessage(message)
47 | .setPositiveButton(R.string.resume, new DialogInterface.OnClickListener() {
48 | @Override
49 | public void onClick(DialogInterface dialog, int which) {
50 | executor.execute();
51 | }
52 | })
53 | .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
54 | @Override
55 | public void onClick(DialogInterface dialog, int which) {
56 | executor.cancel();
57 | }
58 | })
59 | .show();
60 | }
61 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/hosea/messagerelayer/utils/permission/PermissionSetting.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright © Yan Zhenjie
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.hosea.messagerelayer.utils.permission;
17 |
18 | import android.content.Context;
19 | import android.content.DialogInterface;
20 |
21 | import android.text.TextUtils;
22 |
23 |
24 | import androidx.appcompat.app.AlertDialog;
25 |
26 | import com.hosea.messagerelayer.R;
27 | import com.yanzhenjie.permission.AndPermission;
28 | import com.yanzhenjie.permission.Permission;
29 | import com.yanzhenjie.permission.SettingService;
30 |
31 | import java.util.List;
32 |
33 | /**
34 | * Created by YanZhenjie on 2018/1/1.
35 | */
36 | public final class PermissionSetting {
37 |
38 | private final Context mContext;
39 |
40 | public PermissionSetting(Context context) {
41 | this.mContext = context;
42 | }
43 |
44 | public void showSetting(final List permissions) {
45 | List permissionNames = Permission.transformText(mContext, permissions);
46 | String message = mContext.getString(R.string.message_permission_always_failed, TextUtils.join("\n", permissionNames));
47 |
48 | final SettingService settingService = AndPermission.permissionSetting(mContext);
49 | new AlertDialog.Builder(mContext)
50 | .setCancelable(false)
51 | .setTitle(R.string.title_dialog)
52 | .setMessage(message)
53 | .setPositiveButton(R.string.setting, new DialogInterface.OnClickListener() {
54 | @Override
55 | public void onClick(DialogInterface dialog, int which) {
56 | settingService.execute();
57 | }
58 | })
59 | .setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
60 | @Override
61 | public void onClick(DialogInterface dialog, int which) {
62 | settingService.cancel();
63 | }
64 | })
65 | .show();
66 | }
67 | }
--------------------------------------------------------------------------------
/app/src/main/res/anim/accessibility_bottom_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/accessibility_bottom_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_add_contact.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_add_one.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_keyword_four.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_keyword_one.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_keyword_three.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_keyword_two.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/email_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/drawable/email_image.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/drawable/icon_wechat.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/setting_pro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/drawable/setting_pro.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/sms_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/drawable/sms_image.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activitiy_wechat.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
21 |
22 |
27 |
28 |
29 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
89 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_about.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
17 |
24 |
31 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_accessibility_transparent_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_contact_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_keyword.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
12 |
18 |
28 |
29 |
37 |
38 |
49 |
50 |
60 |
61 |
62 |
70 |
76 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_rule.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
25 |
26 |
33 |
34 |
40 |
41 |
42 |
48 |
49 |
55 |
56 |
63 |
64 |
70 |
71 |
72 |
73 |
79 |
80 |
87 |
88 |
93 |
94 |
95 |
96 |
102 |
103 |
110 |
111 |
116 |
117 |
118 |
119 |
127 |
128 |
134 |
135 |
143 |
144 |
153 |
154 |
155 |
161 |
162 |
168 |
169 |
176 |
177 |
186 |
187 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_selected_contact.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
14 |
15 |
28 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_sms_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_sms_relayer.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
76 |
77 |
82 |
83 |
90 |
91 |
96 |
97 |
98 |
105 |
106 |
112 |
113 |
120 |
121 |
130 |
131 |
132 |
133 |
139 |
140 |
147 |
148 |
157 |
158 |
159 |
167 |
168 |
169 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
13 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_email_account.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
14 |
15 |
24 |
25 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_email_servicer.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
17 |
18 |
22 |
23 |
33 |
34 |
38 |
39 |
49 |
50 |
54 |
55 |
65 |
66 |
70 |
71 |
72 |
81 |
82 |
86 |
87 |
96 |
97 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_progress.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
12 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_contact.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
25 |
26 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_keyword.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_selected_contact.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
15 |
16 |
24 |
25 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_sms.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
19 |
20 |
27 |
28 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_about.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/mipmap-xhdpi/ic_about.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/mipmap-xhdpi/ic_add.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_del.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/mipmap-xhdpi/ic_del.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_find.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/mipmap-xhdpi/ic_find.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_ok.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/mipmap-xhdpi/ic_ok.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_remove.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/mipmap-xhdpi/ic_remove.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/mipmap-xhdpi/ic_save.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_selected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/mipmap-xhdpi/ic_selected.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_send_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/mipmap-xhdpi/ic_send_off.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_send_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/mipmap-xhdpi/ic_send_on.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_skip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/mipmap-xhdpi/ic_skip.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_unselected.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/mipmap-xhdpi/ic_unselected.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/app/src/main/res/mipmap-xhdpi/icon.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #379392
4 | #379392
5 | #379392
6 |
7 | #cccaca
8 | #292929
9 | #7c7c7c
10 |
11 | #27ae60
12 | #FFFFFF
13 |
14 | #FC4A1A
15 | #00C07F
16 | #F7B733
17 | #00A6ED
18 |
19 |
20 | #00000000
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 短信多重转发助手
3 | 微信给指定人发消息
4 | 允许以下权限以便程序继续执行:\n\n%1$s
5 | 友情提示
6 | 继续
7 | 设置
8 | 设置
9 | 取消
10 | NO
11 | 我们需要以下权限,请在设置中为我们开启:\n\n%1$s
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
13 |
14 |
15 |
16 |
19 |
20 |
23 |
29 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/accessibility_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/test/java/com/hosea/messagerelayer/ExampleUnitTestMain.java:
--------------------------------------------------------------------------------
1 | package com.hosea.messagerelayer;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTestMain {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:4.1.0'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | maven {
20 | url "https://maven.java.net/content/groups/public/"
21 | }
22 | google()
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | android.enableJetifier=true
13 | android.useAndroidX=true
14 | #org.gradle.jvmargs=-Xmx1536m
15 | org.gradle.jvmargs=--add-opens java.base/java.io=ALL-UNNAMED
16 |
17 | # When configured, Gradle will run in incubating parallel mode.
18 | # This option should only be used with decoupled projects. More details, visit
19 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
20 | # org.gradle.parallel=true
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HoseaDev/SMSRedirectEmail/7c7694a9edab12a159ac93673993048e316de85b/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Nov 28 15:31:50 CST 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------