├── .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 | 26 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 53 | 54 | 55 | 56 | 1.8 57 | 58 | 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 |