├── app
├── .gitignore
├── src
│ └── main
│ │ ├── ic_launcher-web.png
│ │ ├── res
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ └── ic_launcher_foreground.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_launcher_round.png
│ │ │ └── ic_launcher_foreground.png
│ │ ├── xml
│ │ │ ├── files.xml
│ │ │ ├── network_security_config.xml
│ │ │ ├── accessibility_service_config.xml
│ │ │ ├── shortcuts.xml
│ │ │ └── settings_auto_input_code.xml
│ │ ├── values
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── dimens.xml
│ │ │ ├── attrs.xml
│ │ │ ├── colors.xml
│ │ │ └── styles.xml
│ │ ├── layout
│ │ │ ├── fragment_faq.xml
│ │ │ ├── toolbar.xml
│ │ │ ├── item_theme.xml
│ │ │ ├── fragment_code_records.xml
│ │ │ ├── activity_code_rules.xml
│ │ │ ├── activity_code_records.xml
│ │ │ ├── item_permission.xml
│ │ │ ├── fragment_rule_list.xml
│ │ │ ├── activity_home.xml
│ │ │ ├── item_faq.xml
│ │ │ ├── dialog_code_regex_quick_chcoose.xml
│ │ │ ├── item_rule.xml
│ │ │ ├── item_code_record.xml
│ │ │ └── fragment_rule_edit.xml
│ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ ├── menu
│ │ │ ├── context_rule_list.xml
│ │ │ ├── menu_rule_list.xml
│ │ │ ├── menu_edit_code_record.xml
│ │ │ ├── menu_home.xml
│ │ │ └── menu_edit_rule.xml
│ │ ├── drawable
│ │ │ ├── code_rule_quick_choose_bg.xml
│ │ │ ├── ic_add.xml
│ │ │ ├── ic_export.xml
│ │ │ ├── ic_import.xml
│ │ │ ├── ic_done.xml
│ │ │ ├── ic_clear.xml
│ │ │ ├── ic_delete_normal.xml
│ │ │ ├── ic_delete.xml
│ │ │ ├── ic_transform.xml
│ │ │ ├── ic_app_icon.xml
│ │ │ ├── ic_copy.xml
│ │ │ ├── ic_info.xml
│ │ │ ├── ic_time.xml
│ │ │ ├── ic_rating.xml
│ │ │ ├── ic_source_code.xml
│ │ │ ├── ic_focus_mode.xml
│ │ │ ├── ic_input.xml
│ │ │ ├── ic_block.xml
│ │ │ ├── ic_launcher_foreground.xml
│ │ │ ├── ic_notification.xml
│ │ │ ├── ic_help.xml
│ │ │ ├── ic_mark_as_read.xml
│ │ │ ├── ic_lock_open.xml
│ │ │ ├── ic_select_all.xml
│ │ │ ├── ic_records.xml
│ │ │ ├── ic_key_words.xml
│ │ │ ├── ic_palette.xml
│ │ │ ├── ic_alipay.xml
│ │ │ ├── ic_detect.xml
│ │ │ ├── selectable_item_background.xml
│ │ │ ├── ic_toast.xml
│ │ │ ├── ic_log.xml
│ │ │ ├── ic_recently.xml
│ │ │ ├── ic_test.xml
│ │ │ ├── ic_rules.xml
│ │ │ └── ic_automatic.xml
│ │ ├── values-v21
│ │ │ └── styles.xml
│ │ └── values-v27
│ │ │ └── styles.xml
│ │ ├── java
│ │ └── com
│ │ │ └── github
│ │ │ └── tianma8023
│ │ │ └── smscode
│ │ │ ├── backup
│ │ │ ├── ExportResult.java
│ │ │ ├── BackupConst.java
│ │ │ ├── ImportResult.java
│ │ │ ├── exception
│ │ │ │ ├── BackupInvalidException.java
│ │ │ │ ├── VersionMissedException.java
│ │ │ │ └── VersionInvalidException.java
│ │ │ └── RuleExporter.java
│ │ │ ├── app
│ │ │ ├── base
│ │ │ │ ├── back
│ │ │ │ │ ├── BackPressedListener.java
│ │ │ │ │ ├── BackPressFragment.java
│ │ │ │ │ └── BackPressEventDispatchHelper.java
│ │ │ │ ├── BasePreferenceFragment.java
│ │ │ │ └── BaseActivity.java
│ │ │ ├── faq
│ │ │ │ ├── FaqItem.java
│ │ │ │ ├── FaqItemContainer.java
│ │ │ │ ├── FaqItemAdapter.java
│ │ │ │ └── FaqFragment.java
│ │ │ ├── permissions
│ │ │ │ ├── PermItemAdapter.java
│ │ │ │ ├── PermItemContainer.java
│ │ │ │ └── PermItem.java
│ │ │ ├── theme
│ │ │ │ ├── ThemeItemAdapter.java
│ │ │ │ ├── ThemeItem.java
│ │ │ │ └── ThemeItemContainer.java
│ │ │ ├── record
│ │ │ │ ├── RecordItem.java
│ │ │ │ └── CodeRecordsActivity.java
│ │ │ ├── rule
│ │ │ │ ├── RuleAdapter.java
│ │ │ │ └── TemplateRuleManager.java
│ │ │ └── SmsCodeApp.java
│ │ │ ├── adapter
│ │ │ └── OnCreateItemContextMenuListener.java
│ │ │ ├── migrate
│ │ │ ├── ITransition.java
│ │ │ ├── TransitionTask.java
│ │ │ └── PreferencesTransition.java
│ │ │ ├── constant
│ │ │ ├── NotificationConst.java
│ │ │ ├── Const.java
│ │ │ ├── SmsCodeConst.java
│ │ │ └── PrefConst.java
│ │ │ ├── widget
│ │ │ ├── TextWatcherAdapter.java
│ │ │ ├── DialogAsyncTask.java
│ │ │ └── FabScrollBehavior.java
│ │ │ ├── event
│ │ │ ├── XEventBus.java
│ │ │ └── Event.java
│ │ │ ├── utils
│ │ │ ├── SmsMessageUtils.java
│ │ │ ├── rom
│ │ │ │ ├── RomUtils.java
│ │ │ │ └── MiuiUtils.java
│ │ │ ├── ResUtils.java
│ │ │ ├── ClipboardUtils.java
│ │ │ ├── StorageUtils.java
│ │ │ ├── StringUtils.java
│ │ │ ├── SnackbarHelper.java
│ │ │ ├── XLog.java
│ │ │ ├── PackageUtils.java
│ │ │ ├── PreferenceUtils.java
│ │ │ ├── AppOpsUtils.java
│ │ │ ├── Utils.java
│ │ │ ├── ReflectionUtils.java
│ │ │ ├── AccessibilityUtils.java
│ │ │ └── SettingsUtils.java
│ │ │ ├── receiver
│ │ │ ├── BootReceiver.java
│ │ │ └── SmsReceiver.java
│ │ │ ├── db
│ │ │ └── TSQLiteOpenHelper.java
│ │ │ ├── preference
│ │ │ ├── ResetEditPreference.java
│ │ │ └── ResetEditPreferenceDialogFragCompat.java
│ │ │ ├── service
│ │ │ ├── CopyCodeService.java
│ │ │ └── accessibility
│ │ │ │ └── BaseAccessibilityService.java
│ │ │ └── entity
│ │ │ └── SmsMsg.java
│ │ └── assets
│ │ └── logback.xml
├── and-res-guard.gradle
└── proguard-rules.pro
├── settings.gradle
├── art
├── cn
│ ├── 01.png
│ ├── 02.png
│ ├── 03.png
│ ├── 04.png
│ └── 05.png
└── en
│ ├── 01.png
│ ├── 02.png
│ ├── 03.png
│ └── 04.png
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── docs
├── zh-CN
│ ├── appops_adb_help.md
│ └── sms_code_rule_help.md
├── zh-TW
│ ├── appops_adb_help.md
│ └── sms_code_rule_help.md
└── en
│ ├── appops_adb_help.md
│ └── sms_code_rule_help.md
├── gradle.properties
├── .gitignore
├── README-CN.md
├── LOG-CN.md
├── README.md
├── gradlew.bat
└── LOG-EN.md
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /release
3 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/art/cn/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/art/cn/01.png
--------------------------------------------------------------------------------
/art/cn/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/art/cn/02.png
--------------------------------------------------------------------------------
/art/cn/03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/art/cn/03.png
--------------------------------------------------------------------------------
/art/cn/04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/art/cn/04.png
--------------------------------------------------------------------------------
/art/cn/05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/art/cn/05.png
--------------------------------------------------------------------------------
/art/en/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/art/en/01.png
--------------------------------------------------------------------------------
/art/en/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/art/en/02.png
--------------------------------------------------------------------------------
/art/en/03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/art/en/03.png
--------------------------------------------------------------------------------
/art/en/04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/art/en/04.png
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tianma8023/SmsCode/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/xml/files.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #43A047
4 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/backup/ExportResult.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.backup;
2 |
3 | public enum ExportResult {
4 | SUCCESS,
5 | FAILED
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/base/back/BackPressedListener.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.base.back;
2 |
3 | interface BackPressedListener {
4 |
5 | boolean onInterceptBackPressed();
6 |
7 | void onBackPressed();
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Jan 12 13:42:39 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_faq.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/context_rule_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/adapter/OnCreateItemContextMenuListener.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.adapter;
2 |
3 | import android.view.ContextMenu;
4 | import android.view.View;
5 |
6 | public interface OnCreateItemContextMenuListener {
7 |
8 | void onCreateItemContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo, int position);
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/code_rule_quick_choose_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_add.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_export.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_import.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_done.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 2dp
5 | 6dp
6 | 8dp
7 | 12dp
8 | 16dp
9 | 20dp
10 | 24dp
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/migrate/ITransition.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.migrate;
2 |
3 | /**
4 | * 数据迁移过渡接口(版本更新不免遇到旧数据被弃用之类的,兼容旧版本的话就需要进行数据迁移)
5 | */
6 | public interface ITransition {
7 |
8 | /**
9 | * 是否需要迁移过渡数据
10 | */
11 | boolean shouldTransit();
12 |
13 | /**
14 | * 执行数据迁移兼容过渡逻辑
15 | * @return 是否执行成功,是则返回true
16 | */
17 | boolean doTransition();
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/constant/NotificationConst.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.constant;
2 |
3 | public interface NotificationConst {
4 |
5 | int NOTIFICATION_ID_FOREGROUND_SVC = 0xff;
6 | String CHANNEL_ID_FOREGROUND_SERVICE = "foreground_service";
7 |
8 | String CHANNEL_ID_SMSCODE_NOTIFICATION = "smscode_notification";
9 | String GROUP_KEY_SMSCODE_NOTIFICATION = "group_key_smscode_notification";
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_clear.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_delete_normal.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_delete.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/accessibility_service_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v27/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_transform.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/zh-CN/appops_adb_help.md:
--------------------------------------------------------------------------------
1 | 短信权限 ADB 设置帮助
2 | --------
3 |
4 | - 权限说明
5 |
6 | 自 Android 4.4 起,非默认短信应用修改短信受到限制。除了需要授予写短信权限外,还需要修改 `AppOps` 的设置给予 `WRITE_SMS` 权限。
7 |
8 | - ADB 设置
9 | 1. 在电脑上配置ADB环境;
10 | 2. 打开手机 `开发者选项` 和 `USB调试`,并连接电脑;
11 | 3. 执行 ADB 命令:
12 | ```shell
13 | adb shell appops set com.github.tianma8023.smscode WRITE_SMS allow
14 | ```
15 | 4. 重新打开 `标记为已读` 开关,检查是否已经设置成功。
16 |
17 | - 限制
18 |
19 | 如果您设备上默认的短信 App 使用的是它自己的短信数据库,而不是系统的短信数据库,那么即便给予了上述权限,也有可能不会生效。如果发生这样的事情,就说明可能在您的设备上可能不会生效。
20 |
21 |
--------------------------------------------------------------------------------
/docs/zh-TW/appops_adb_help.md:
--------------------------------------------------------------------------------
1 | 簡訊權限 ADB 設定幫助
2 | --------
3 |
4 | - 權限說明
5 |
6 | 自 Android 4.4 起,非默認簡訊應用修改簡訊受到限制。除了需要授予寫簡訊權限外,還需要修改 `AppOps` 的設置給予 `WRITE_SMS` 權限。
7 |
8 | - ADB 設定
9 | 1. 在電腦上配置 ADB 環境;
10 | 2. 打開手機的 `開發者選項` 和 `USB偵錯`,并連接至電腦;
11 | 3. 執行 ADB 命令:
12 | ```shell
13 | adb shell appops set com.github.tianma8023.smscode WRITE_SMS allow
14 | ```
15 | 4. 重新打開應用中的 `標記為已讀`,檢查是否已經設定成功。
16 |
17 | - 限制
18 |
19 | 如果您設備上默認的簡訊 App 使用的是它自己的簡訊數據庫,而不是系統的簡訊數據庫,那麽即便給予了上述權限,也有可能不會生效。如果發生這樣的事情,就說明可能在您的設備上可能不會生效。
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/backup/BackupConst.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.backup;
2 |
3 | public class BackupConst {
4 |
5 | public static final String KEY_VERSION = "version";
6 | public static final int BACKUP_VERSION = 1;
7 |
8 | public static final String KEY_RULES = "rules";
9 |
10 | public static final String KEY_COMPANY = "company";
11 | public static final String KEY_CODE_KEYWORD = "code_keyword";
12 | public static final String KEY_CODE_REGEX = "code_regex";
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/backup/ImportResult.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.backup;
2 |
3 | public enum ImportResult {
4 | /**
5 | * Success
6 | */
7 | SUCCESS,
8 | /**
9 | * Backup version missed
10 | */
11 | VERSION_MISSED,
12 | /**
13 | * Backup version unknown
14 | */
15 | VERSION_UNKNOWN,
16 | /**
17 | * Backup invalid
18 | */
19 | BACKUP_INVALID,
20 | /**
21 | * Read error
22 | */
23 | READ_FAILED,
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_app_icon.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/base/BasePreferenceFragment.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.base;
2 |
3 | import androidx.annotation.NonNull;
4 | import androidx.preference.Preference;
5 | import androidx.preference.PreferenceFragmentCompat;
6 |
7 | public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
8 |
9 | @Override
10 | @NonNull
11 | public T findPreference(@NonNull CharSequence key) {
12 | return super.findPreference(key);
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_copy.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_info.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/base/back/BackPressFragment.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.base.back;
2 |
3 | import androidx.fragment.app.Fragment;
4 |
5 | /**
6 | * Fragment that handled back pressed event.
7 | */
8 | public class BackPressFragment extends Fragment implements BackPressedListener {
9 |
10 | @Override
11 | public boolean onInterceptBackPressed() {
12 | return false;
13 | }
14 |
15 | @Override
16 | public void onBackPressed() {
17 | BackPressEventDispatchHelper.dispatchBackPressedEvent(this);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_time.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_rating.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_source_code.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_focus_mode.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_input.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_rule_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/backup/exception/BackupInvalidException.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.backup.exception;
2 |
3 | /**
4 | * Backup invalid exception
5 | */
6 | public class BackupInvalidException extends Exception {
7 |
8 | public BackupInvalidException() {
9 | }
10 |
11 | public BackupInvalidException(String message) {
12 | super(message);
13 | }
14 |
15 | public BackupInvalidException(String message, Throwable cause) {
16 | super(message, cause);
17 | }
18 |
19 | public BackupInvalidException(Throwable cause) {
20 | super(cause);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_block.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_notification.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_edit_code_record.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_help.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/backup/exception/VersionMissedException.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.backup.exception;
2 |
3 | /**
4 | * version missed exception
5 | */
6 | public class VersionMissedException extends VersionInvalidException {
7 |
8 | public VersionMissedException() {
9 | }
10 |
11 | public VersionMissedException(String message) {
12 | super(message);
13 | }
14 |
15 | public VersionMissedException(String message, Throwable cause) {
16 | super(message, cause);
17 | }
18 |
19 | public VersionMissedException(Throwable cause) {
20 | super(cause);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_mark_as_read.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/backup/exception/VersionInvalidException.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.backup.exception;
2 |
3 | /**
4 | * Version invalid exception
5 | */
6 | public class VersionInvalidException extends BackupInvalidException {
7 |
8 | public VersionInvalidException() {
9 | }
10 |
11 | public VersionInvalidException(String message) {
12 | super(message);
13 | }
14 |
15 | public VersionInvalidException(String message, Throwable cause) {
16 | super(message, cause);
17 | }
18 |
19 | public VersionInvalidException(Throwable cause) {
20 | super(cause);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/widget/TextWatcherAdapter.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.widget;
2 |
3 | import android.text.Editable;
4 | import android.text.TextWatcher;
5 |
6 | /**
7 | * Default text watcher implementation
8 | */
9 | public class TextWatcherAdapter implements TextWatcher{
10 |
11 | @Override
12 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
13 |
14 | }
15 |
16 | @Override
17 | public void onTextChanged(CharSequence s, int start, int before, int count) {
18 |
19 | }
20 |
21 | @Override
22 | public void afterTextChanged(Editable s) {
23 |
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_lock_open.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/event/XEventBus.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.event;
2 |
3 | import org.greenrobot.eventbus.EventBus;
4 |
5 | /**
6 | * Event bus utils
7 | */
8 | public class XEventBus {
9 |
10 | private XEventBus() {
11 | }
12 |
13 | private static EventBus get() {
14 | return EventBus.getDefault();
15 | }
16 |
17 | public static void post(Object event) {
18 | get().post(event);
19 | }
20 |
21 | public static void register(Object subscriber) {
22 | get().register(subscriber);
23 | }
24 |
25 | public static void unregister(Object subscriber) {
26 | get().unregister(subscriber);
27 | }
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/faq/FaqItem.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.faq;
2 |
3 | public class FaqItem {
4 | private String question;
5 | private String answer;
6 |
7 | FaqItem(String question, String answer) {
8 | this.question = question;
9 | this.answer = answer;
10 | }
11 |
12 | String getQuestion() {
13 | return question;
14 | }
15 |
16 | String getAnswer() {
17 | return answer;
18 | }
19 |
20 | @Override
21 | public String toString() {
22 | return "FaqItem{" +
23 | "question='" + question + '\'' +
24 | ", answer='" + answer + '\'' +
25 | '}';
26 | }
27 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_select_all.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_theme.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_records.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_edit_rule.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/constant/Const.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.constant;
2 |
3 |
4 | public interface Const {
5 |
6 | String PROJECT_SOURCE_CODE_URL = "https://github.com/tianma8023/SmsCode";
7 |
8 | String ALIPAY_PACKAGE_NAME = "com.eg.android.AlipayGphone";
9 | String ALIPAY_QRCODE_URI_PREFIX = "alipayqr://platformapi/startapp?saId=10000007&qrcode=";
10 | String ALIPAY_QRCODE_URL = "HTTPS://QR.ALIPAY.COM/FKX074142EKXD0OIMV8B60";
11 | String ALIPAY_RED_PACKET_CODE = "637183313";
12 |
13 | String COOL_MARKET_PACKAGE_NAME = "com.coolapk.market";
14 |
15 | String PROJECT_DOC_BASE_URL = "https://tianma8023.github.io/SmsCode";
16 | String DOC_APPOPS_ADB_HELP = "appops_adb_help";
17 | String DOC_SMS_CODE_RULE_HELP ="sms_code_rule_help";
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/constant/SmsCodeConst.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.constant;
2 |
3 | public interface SmsCodeConst {
4 |
5 | String VERIFICATION_KEYWORDS_REGEX =
6 | /**/ "验证码|校验码|检验码|确认码|激活码|动态码|安全码" +
7 | /**/ "|验证代码|校验代码|检验代码|激活代码|确认代码|动态代码|安全代码" +
8 | /**/ "|登入码|认证码|识别码" +
9 | /**/ "|短信口令|动态密码|交易码|上网密码|随机码|动态口令" +
10 | /**/ "|驗證碼|校驗碼|檢驗碼|確認碼|激活碼|動態碼" +
11 | /**/ "|驗證代碼|校驗代碼|檢驗代碼|確認代碼|激活代碼|動態代碼" +
12 | /**/ "|登入碼|認證碼|識別碼" +
13 | /**/ "|Code|code|CODE";
14 |
15 | String PHONE_NUMBER_KEYWORDS =
16 | /**/ "手机号|电话号" +
17 | /**/ "|手機號|電話號" +
18 | /*(?i) 表示忽略大小写*/ "|(?i)phone(?-i)" +
19 | /*(?i) 表示忽略大小写*/ "|(?i)number(?-i)";
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_key_words.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/permissions/PermItemAdapter.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.permissions;
2 |
3 | import com.chad.library.adapter.base.BaseQuickAdapter;
4 | import com.chad.library.adapter.base.BaseViewHolder;
5 | import com.github.tianma8023.smscode.R;
6 |
7 | import java.util.List;
8 |
9 | import androidx.annotation.Nullable;
10 |
11 | public class PermItemAdapter extends BaseQuickAdapter {
12 |
13 | public PermItemAdapter(@Nullable List data) {
14 | super(R.layout.item_permission, data);
15 | }
16 |
17 | @Override
18 | protected void convert(BaseViewHolder helper, PermItem item) {
19 | helper.setText(R.id.tv_perm_title, item.getTitle())
20 | .setText(R.id.tv_perm_content, item.getContent());
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_code_records.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
19 |
20 |
--------------------------------------------------------------------------------
/docs/en/appops_adb_help.md:
--------------------------------------------------------------------------------
1 | AppOpsManager ADB setting help
2 | --------
3 |
4 | - Permission statement
5 |
6 | Writing SMS for Non-default SMS app has been limited since Android 4.4. Except for granting write SMS runtime permission, `WRITE_SMS` permission in AppOpsManager also should be granted.
7 |
8 | - ADB settings
9 | 1. Install ADB environment in your computer.
10 | 2. Open `Developer options` and `USB debugging` on your phone and then link to the computer.
11 | 3. Execute ADB command:
12 | ```shell
13 | adb shell appops set com.github.tianma8023.smscode WRITE_SMS allow
14 | ```
15 | 4. Reopen `Mark as read` switch to check whether permission is granted or not.
16 |
17 | - Restrictions
18 |
19 | It may be invlid if the default system SMS app on your phone uses its own SMS database which is different from system SMS database, although the settings above have been applied.
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_code_rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_code_records.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/SmsMessageUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils;
2 |
3 | import android.telephony.SmsMessage;
4 |
5 | /**
6 | * Utils about android.telephony.SmsMessage
7 | */
8 | public class SmsMessageUtils {
9 |
10 | private static final int SMS_CHARACTER_LIMIT = 160;
11 |
12 | private SmsMessageUtils() {
13 | }
14 |
15 | public static String getMessageBody(SmsMessage[] messageParts) {
16 | if (messageParts.length == 1) {
17 | return messageParts[0].getDisplayMessageBody();
18 | } else {
19 | StringBuilder sb = new StringBuilder(SMS_CHARACTER_LIMIT * messageParts.length);
20 | for (SmsMessage messagePart : messageParts) {
21 | sb.append(messagePart.getDisplayMessageBody());
22 | }
23 | return sb.toString();
24 | }
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/migrate/TransitionTask.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.migrate;
2 |
3 | import android.content.Context;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | /**
9 | * 执行数据迁移过渡的Task
10 | */
11 | public class TransitionTask implements Runnable {
12 |
13 | private List mTransitionList;
14 |
15 | public TransitionTask(Context context) {
16 | init(context);
17 | }
18 |
19 | private void init(Context context) {
20 | mTransitionList = new ArrayList<>();
21 | mTransitionList.add(new PreferencesTransition(context));
22 | }
23 |
24 | @Override
25 | public void run() {
26 | for (ITransition transition : mTransitionList) {
27 | if (transition.shouldTransit()) {
28 | transition.doTransition();
29 | }
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/docs/zh-CN/sms_code_rule_help.md:
--------------------------------------------------------------------------------
1 | 自定义验证码规则帮助
2 | --------
3 |
4 | - 当默认的验证码解析规则不能正确解析某些特殊短信中的验证码时,就可以使用自定义规则。
5 | - 自定义验证码规则包含三个部分:公司或组织名,验证码关键字,验证码正则表达式。
6 |
7 | 温馨提示:**只要上述 3 个部分所组成的规则能正确识别指定验证码短信即可,不一定要拘泥于格式**
8 |
9 | 例子:
10 | - `请使用 192830 验证你的 Instagram 账户`
11 | 1. 公司或组织名可以是:`Instagram`(推荐), `Instagram 账户` 等,默认忽略大小写
12 | 2. 验证码关键字可以是:`验证`(推荐), `验证你`, `验证你的` 等,默认忽略大小写
13 | 3. 验证码正则表达式: `(?
7 |
10 |
11 |
--------------------------------------------------------------------------------
/docs/en/sms_code_rule_help.md:
--------------------------------------------------------------------------------
1 | Custom SMS code rule help
2 | --------
3 |
4 | - You can custom SMS code rules when default algorithm of parsing SMS code cannot meet special needs.
5 | - SMS code rule contains 3 parts: company, code keyword and code regular expression.
6 |
7 | Note that:
8 | - **As long as the rules formed by the above three parts can correctly identify the specified verification code SMS, there is no need to stick to the format. That is, there are different ways to define the rule for a certain SMS.**
9 | - For the usage of Regular Expression, you can search on Google by yourself.
10 |
11 | Example:
12 | 1.
13 | ```text
14 | [Google] Your verification code is 198901
15 | ```
16 | - Company can be: `Google`(Recommended), `[Google]` etc. Case insensitive by default.
17 | - Code keyword can be: `verification code`(Recommended), `code` etc. Case insenditive by default.
18 | - Code regular expression: `(?
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/faq/FaqItemContainer.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.faq;
2 |
3 | import android.content.Context;
4 | import android.content.res.Resources;
5 |
6 | import com.github.tianma8023.smscode.R;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 |
12 | public class FaqItemContainer {
13 |
14 | private List mFaqItems = new ArrayList<>();
15 |
16 | FaqItemContainer(Context context) {
17 | loadItems(context);
18 | }
19 |
20 | private void loadItems(Context context) {
21 | Resources res = context.getResources();
22 | String[] questionArr = res.getStringArray(R.array.question_list);
23 | String[] answerArr = res.getStringArray(R.array.answer_list);
24 | for (int i = 0; i < questionArr.length; i++) {
25 | mFaqItems.add(new FaqItem(questionArr[i], answerArr[i]));
26 | }
27 | }
28 |
29 | List getFaqItems() {
30 | return mFaqItems;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/theme/ThemeItemAdapter.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.theme;
2 |
3 | import android.content.Context;
4 |
5 | import com.chad.library.adapter.base.BaseQuickAdapter;
6 | import com.chad.library.adapter.base.BaseViewHolder;
7 | import com.github.tianma8023.smscode.R;
8 |
9 | import java.util.List;
10 |
11 | import androidx.core.content.ContextCompat;
12 |
13 | public class ThemeItemAdapter extends BaseQuickAdapter {
14 |
15 | private Context mContext;
16 |
17 | public ThemeItemAdapter(Context context, List themeItemList) {
18 | super(R.layout.item_theme, themeItemList);
19 | mContext = context;
20 | }
21 |
22 | @Override
23 | protected void convert(BaseViewHolder helper, ThemeItem item) {
24 | helper.setBackgroundColor(R.id.tv_color_item, ContextCompat.getColor(mContext, item.getColorValueRes()))
25 | .setText(R.id.tv_color_item, item.getColorNameRes());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_detect.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/selectable_item_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | -
5 |
6 |
-
7 |
8 |
9 |
10 |
11 |
12 |
14 |
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | android.enableJetifier=true
10 | android.useAndroidX=true
11 | org.gradle.jvmargs=-Xmx1536m
12 | # When configured, Gradle will run in incubating parallel mode.
13 | # This option should only be used with decoupled projects. More details, visit
14 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
15 | # org.gradle.parallel=true
16 |
17 | # Custom Properties start
18 | tianma.keystore.path=/Users/tianma/Codes/Keystore/tianma.jks
19 | tianma.signature.path=/Users/tianma/Codes/Keystore/signature.properties
20 | # Custom Properties end
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/permissions/PermItemContainer.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.permissions;
2 |
3 | import android.content.Context;
4 | import android.content.res.Resources;
5 |
6 | import com.github.tianma8023.smscode.R;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | public class PermItemContainer {
12 |
13 | private List mItems = new ArrayList<>();
14 |
15 | public PermItemContainer(Context context) {
16 | loadItems(context);
17 | }
18 |
19 | private void loadItems(Context context) {
20 | Resources res = context.getResources();
21 | String[] titles = res.getStringArray(R.array.perm_title_list);
22 | String[] contents = res.getStringArray(R.array.perm_content_list);
23 | for(int i = 0; i < titles.length; i++) {
24 | mItems.add(new PermItem(titles[i], contents[i]));
25 | }
26 | }
27 |
28 | public List getItems() {
29 | return mItems;
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_permission.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
21 |
22 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_toast.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/base/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.base;
2 |
3 | import android.os.Bundle;
4 |
5 | import com.github.tianma8023.smscode.app.theme.ThemeItemContainer;
6 | import com.github.tianma8023.smscode.utils.SPUtils;
7 |
8 | import androidx.annotation.Nullable;
9 | import androidx.appcompat.app.AppCompatActivity;
10 |
11 | public abstract class BaseActivity extends AppCompatActivity {
12 |
13 | @Override
14 | protected void onCreate(@Nullable Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | initTheme();
17 | }
18 |
19 | private void initTheme() {
20 | int index = SPUtils.getCurrentThemeIndex(this);
21 | setTheme(ThemeItemContainer.get().getItemAt(index).getThemeRes());
22 | }
23 |
24 | // @Override
25 | // protected void onResume() {
26 | // super.onResume();
27 | //// MobclickAgent.onResume(this);
28 | // }
29 |
30 |
31 | // @Override
32 | // protected void onPause() {
33 | // super.onPause();
34 | //// MobclickAgent.onPause(this);
35 | // }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/faq/FaqItemAdapter.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.faq;
2 |
3 | import android.content.Context;
4 |
5 | import com.chad.library.adapter.base.BaseQuickAdapter;
6 | import com.chad.library.adapter.base.BaseViewHolder;
7 | import com.github.tianma8023.smscode.R;
8 |
9 | import java.util.List;
10 |
11 | public class FaqItemAdapter extends BaseQuickAdapter {
12 | private final String mQuestionPrefix;
13 | private final String mAnswerPrefix;
14 |
15 | FaqItemAdapter(Context context, List items) {
16 | super(R.layout.item_faq, items);
17 | mQuestionPrefix = context.getString(R.string.simplified_question);
18 | mAnswerPrefix = context.getString(R.string.simplified_answer);
19 | }
20 |
21 | @Override
22 | protected void convert(BaseViewHolder helper, FaqItem item) {
23 | String question = mQuestionPrefix + item.getQuestion();
24 | String answer = mAnswerPrefix + item.getAnswer();
25 | helper.setText(R.id.item_question, question)
26 | .setText(R.id.item_answer, answer);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_log.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_rule_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
19 |
20 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_recently.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/record/RecordItem.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.record;
2 |
3 | import com.github.tianma8023.smscode.entity.SmsMsg;
4 |
5 | import java.util.Objects;
6 |
7 | public class RecordItem {
8 |
9 | private SmsMsg smsMsg;
10 | private boolean selected;
11 |
12 | RecordItem(SmsMsg smsMsg) {
13 | this.smsMsg = smsMsg;
14 | }
15 |
16 | public SmsMsg getSmsMsg() {
17 | return smsMsg;
18 | }
19 |
20 | public boolean isSelected() {
21 | return selected;
22 | }
23 |
24 | public void setSelected(boolean selected) {
25 | this.selected = selected;
26 | }
27 |
28 | @Override
29 | public String toString() {
30 | return "RecordItem{" +
31 | "smsMsg=" + smsMsg +
32 | ", selected=" + selected +
33 | '}';
34 | }
35 |
36 | @Override
37 | public boolean equals(Object o) {
38 | if (this == o) return true;
39 | if (!(o instanceof RecordItem)) return false;
40 | RecordItem item = (RecordItem) o;
41 | return Objects.equals(smsMsg, item.smsMsg);
42 | }
43 |
44 | @Override
45 | public int hashCode() {
46 | return Objects.hash(smsMsg);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
24 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_faq.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
19 |
20 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/rom/RomUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils.rom;
2 |
3 | import android.text.TextUtils;
4 |
5 | import java.io.BufferedReader;
6 | import java.io.IOException;
7 | import java.io.InputStreamReader;
8 |
9 |
10 | /**
11 | * Utils for check Chinese ROM.
12 | */
13 | public class RomUtils {
14 |
15 | private RomUtils() {
16 |
17 | }
18 |
19 | public static boolean isMiui() {
20 | return !TextUtils.isEmpty(getSystemProperty(MiuiUtils.KEY_VERSION_NAME_MIUI));
21 | }
22 |
23 | public static String getSystemProperty(String propName) {
24 | String result = null;
25 | BufferedReader br = null;
26 | try {
27 | Process p = Runtime.getRuntime().exec("getprop " + propName);
28 | br = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
29 | result = br.readLine();
30 | } catch (IOException e) {
31 | e.printStackTrace();
32 | } finally {
33 | if (br != null) {
34 | try {
35 | br.close();
36 | } catch (IOException e) {
37 | e.printStackTrace();
38 | }
39 | }
40 | }
41 | return result;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/ResUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils;
2 |
3 | import android.content.Context;
4 |
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 |
8 | import androidx.annotation.RawRes;
9 |
10 | public class ResUtils {
11 |
12 | private ResUtils() {
13 |
14 | }
15 |
16 | /**
17 | * load data from raw resources.
18 | * @param context context
19 | * @param rawId raw file id.
20 | * @return raw file text content.
21 | */
22 | public static String loadRawRes(Context context, @RawRes int rawId) {
23 | InputStream is = null;
24 | String data = "";
25 | try {
26 | is = context.getResources().openRawResource(rawId);
27 | byte[] buffer = new byte[is.available()];
28 | is.read(buffer);
29 | data = new String(buffer);
30 | } catch (IOException e) {
31 | XLog.e("Error occurs when open raw file, id = " + rawId, e);
32 | } finally {
33 | if (is != null) {
34 | try {
35 | is.close();
36 | } catch (IOException e) {
37 | e.printStackTrace();
38 | }
39 | }
40 | }
41 | return data;
42 | }
43 |
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_code_regex_quick_chcoose.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
19 |
20 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_test.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/permissions/PermItem.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.permissions;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | public class PermItem implements Parcelable {
7 |
8 | private String title;
9 |
10 | private String content;
11 |
12 | PermItem(String title, String content) {
13 | this.title = title;
14 | this.content = content;
15 | }
16 |
17 | protected PermItem(Parcel in) {
18 | title = in.readString();
19 | content = in.readString();
20 | }
21 |
22 | public String getTitle() {
23 | return title;
24 | }
25 |
26 | public String getContent() {
27 | return content;
28 | }
29 |
30 | @Override
31 | public int describeContents() {
32 | return 0;
33 | }
34 |
35 | @Override
36 | public void writeToParcel(Parcel dest, int flags) {
37 | dest.writeString(title);
38 | dest.writeString(content);
39 | }
40 |
41 | public static final Creator CREATOR = new Creator() {
42 | @Override
43 | public PermItem createFromParcel(Parcel in) {
44 | return new PermItem(in);
45 | }
46 |
47 | @Override
48 | public PermItem[] newArray(int size) {
49 | return new PermItem[size];
50 | }
51 | };
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/widget/DialogAsyncTask.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.widget;
2 |
3 | import android.content.Context;
4 | import android.content.DialogInterface;
5 | import android.os.AsyncTask;
6 |
7 | import com.afollestad.materialdialogs.MaterialDialog;
8 |
9 | public abstract class DialogAsyncTask extends AsyncTask implements DialogInterface.OnCancelListener {
10 |
11 | private boolean mCancelable;
12 |
13 | private MaterialDialog mProgressDialog;
14 |
15 | public DialogAsyncTask(Context context, String progressMsg, boolean cancelable) {
16 | mProgressDialog = new MaterialDialog.Builder(context)
17 | .content(progressMsg)
18 | .progress(true, 100)
19 | .cancelable(mCancelable)
20 | .build();
21 | mCancelable = cancelable;
22 | }
23 |
24 | @Override
25 | protected void onPreExecute() {
26 | if (mCancelable) {
27 | mProgressDialog.setOnCancelListener(this);
28 | }
29 | mProgressDialog.show();
30 | }
31 |
32 | @Override
33 | public void onCancel(DialogInterface dialog) {
34 | cancel(true);
35 | }
36 |
37 | @Override
38 | protected void onPostExecute(Result result) {
39 | mProgressDialog.dismiss();
40 | }
41 |
42 | @Override
43 | protected void onCancelled() {
44 | mProgressDialog.dismiss();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/app/src/main/assets/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %level: %msg%n
13 | ${CHARSET}
14 |
15 |
16 |
17 |
18 | true
19 |
20 |
21 | ${LOG_DIR}/%d{yyyy-MM-dd}.log
22 | ${MAX_HISTORY}
23 |
24 |
25 |
26 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %level: %msg%n%exception{full}
27 | ${CHARSET}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/faq/FaqFragment.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.faq;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 |
9 | import com.github.tianma8023.smscode.R;
10 |
11 | import java.util.List;
12 |
13 | import androidx.annotation.NonNull;
14 | import androidx.fragment.app.Fragment;
15 | import androidx.recyclerview.widget.LinearLayoutManager;
16 | import androidx.recyclerview.widget.RecyclerView;
17 |
18 | /**
19 | * FAQ fragment
20 | */
21 | public class FaqFragment extends Fragment {
22 |
23 | public FaqFragment() {
24 | }
25 |
26 | public static FaqFragment newInstance() {
27 | return new FaqFragment();
28 | }
29 |
30 | @Override
31 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
32 | Bundle savedInstanceState) {
33 | View view = inflater.inflate(R.layout.fragment_faq, container, false);
34 |
35 | // Set the adapter
36 | if (view instanceof RecyclerView) {
37 | Context context = view.getContext();
38 | RecyclerView recyclerView = (RecyclerView) view;
39 | recyclerView.setLayoutManager(new LinearLayoutManager(context));
40 | List items = new FaqItemContainer(context).getFaqItems();
41 | recyclerView.setAdapter(new FaqItemAdapter(context, items));
42 | }
43 | return view;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/receiver/BootReceiver.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.receiver;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | import com.github.tianma8023.smscode.constant.PrefConst;
8 | import com.github.tianma8023.smscode.service.SmsObserveService;
9 | import com.github.tianma8023.smscode.utils.SPUtils;
10 | import com.github.tianma8023.smscode.utils.XLog;
11 |
12 | /**
13 | * Start SMS observe service if necessary after booting completed.
14 | */
15 | public class BootReceiver extends BroadcastReceiver {
16 |
17 | @Override
18 | public void onReceive(Context context, Intent intent) {
19 | boolean enable = SPUtils.isEnable(context);
20 | String listenMode = SPUtils.getListenMode(context);
21 | if (enable && PrefConst.LISTEN_MODE_COMPATIBLE.equals(listenMode)) {
22 | XLog.d("BootReceiver received: {}", intent.getAction());
23 | boolean isVerboseLog = SPUtils.isVerboseLogMode(context);
24 | try {
25 | SmsObserveService.startMe(context, isVerboseLog);
26 | } catch (Exception e) {
27 | // 未置为电池优化白名单
28 | // Not allowed to start service Intent { cmp=com.github.tianma8023.smscode/.service.SmsObserveService (has extras) }: app is in background uid UidRecord
29 | // 有两种解决方案:
30 | // 1. 利用 JobService 处理,但是实时性不好,不适用于本方案
31 | // 2. try catch
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/db/TSQLiteOpenHelper.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.db;
2 |
3 | import android.content.Context;
4 | import android.database.sqlite.SQLiteDatabase;
5 |
6 | import com.github.tianma8023.smscode.entity.DaoMaster;
7 | import com.github.tianma8023.smscode.entity.SmsCodeRuleDao;
8 | import com.github.tianma8023.smscode.entity.SmsMsgDao;
9 | import com.github.yuweiguocn.library.greendao.MigrationHelper;
10 |
11 | import org.greenrobot.greendao.database.Database;
12 |
13 | public class TSQLiteOpenHelper extends DaoMaster.OpenHelper{
14 |
15 | public TSQLiteOpenHelper(Context context, String name) {
16 | super(context, name);
17 | }
18 |
19 | public TSQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
20 | super(context, name, factory);
21 | }
22 |
23 | @SuppressWarnings("unchecked")
24 | @Override
25 | public void onUpgrade(Database db, int oldVersion, int newVersion) {
26 | super.onUpgrade(db, oldVersion, newVersion);
27 |
28 | MigrationHelper.migrate(db, new MigrationHelper.ReCreateAllTableListener() {
29 | @Override
30 | public void onCreateAllTables(Database db, boolean ifNotExists) {
31 | DaoMaster.createAllTables(db, ifNotExists);
32 | }
33 |
34 | @Override
35 | public void onDropAllTables(Database db, boolean ifExists) {
36 | DaoMaster.dropAllTables(db, ifExists);
37 | }
38 | }, SmsCodeRuleDao.class, SmsMsgDao.class);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/libraries
5 | /.idea/modules.xml
6 | /.idea/workspace.xml
7 | .DS_Store
8 | /build
9 | /captures
10 | .externalNativeBuild
11 |
12 | # Built application files
13 | *.apk
14 | *.ap_
15 |
16 | # Files for the ART/Dalvik VM
17 | *.dex
18 |
19 | # Java class files
20 | *.class
21 |
22 | # Generated files
23 | bin/
24 | gen/
25 | out/
26 |
27 | # Gradle files
28 | .gradle/
29 | build/
30 |
31 | # Local configuration file (sdk path, etc)
32 | local.properties
33 |
34 | # Proguard folder generated by Eclipse
35 | proguard/
36 |
37 | # Log Files
38 | *.log
39 |
40 | # Android Studio Navigation editor temp files
41 | .navigation/
42 |
43 | # Android Studio captures folder
44 | captures/
45 |
46 | # IntelliJ
47 | *.iml
48 | .idea/workspace.xml
49 | .idea/tasks.xml
50 | .idea/gradle.xml
51 | .idea/assetWizardSettings.xml
52 | .idea/dictionaries
53 | .idea/libraries
54 | .idea/caches
55 | .idea/*
56 |
57 | # Keystore files
58 | # Uncomment the following line if you do not want to check your keystore files in.
59 | *.jks
60 |
61 | # External native build folder generated in Android Studio 2.2 and later
62 | .externalNativeBuild
63 |
64 | # Google Services (e.g. APIs or Firebase)
65 | google-services.json
66 |
67 | # Freeline
68 | freeline.py
69 | freeline/
70 | freeline_project_description.json
71 |
72 | # fastlane
73 | fastlane/report.xml
74 | fastlane/Preview.html
75 | fastlane/screenshots
76 | fastlane/test_output
77 | fastlane/readme.md
78 |
79 | # signature
80 | app/signature.properties
81 |
82 | # eclipse
83 | .settings/
84 | *.project
85 | */.classpath
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_rule.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
22 |
23 |
31 |
32 |
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/ClipboardUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils;
2 |
3 | import android.content.ClipData;
4 | import android.content.ClipDescription;
5 | import android.content.ClipboardManager;
6 | import android.content.Context;
7 |
8 | /**
9 | * Clipboard utils.
10 | */
11 | public class ClipboardUtils {
12 |
13 | private ClipboardUtils() {
14 | }
15 |
16 | public static void copyToClipboard(Context context, String text) {
17 | try {
18 | ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
19 | if (cm == null) {
20 | return;
21 | }
22 | ClipData clipData = ClipData.newPlainText("Copy text", text);
23 | cm.setPrimaryClip(clipData);
24 | XLog.d("Copy {} to clipboard", text);
25 | } catch (Throwable t) {
26 | XLog.e("Error occurs when copy to clipboard", t);
27 | }
28 | }
29 |
30 | public static void clearClipboard(Context context) {
31 | ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
32 | if (cm == null) {
33 | return;
34 | }
35 | if (cm.hasPrimaryClip()) {
36 | ClipDescription cd = cm.getPrimaryClipDescription();
37 | if (cd != null) {
38 | if (cd.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
39 | cm.setPrimaryClip(ClipData.newPlainText("Copy text", ""));
40 | }
41 | }
42 | }
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/StorageUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils;
2 |
3 | import android.content.Context;
4 | import android.os.Environment;
5 |
6 | import java.io.File;
7 |
8 | /**
9 | * Utils for storage.
10 | */
11 | public class StorageUtils {
12 |
13 | private StorageUtils() {
14 |
15 | }
16 |
17 | public static boolean isSDCardMounted() {
18 | String state = Environment.getExternalStorageState();
19 | return Environment.MEDIA_MOUNTED.equals(state);
20 | }
21 |
22 | /**
23 | * 获取日志路径
24 | */
25 | public static File getLogDir(Context context) {
26 | if (isSDCardMounted()) {
27 | return context.getExternalFilesDir("log");
28 | } else {
29 | return new File(context.getFilesDir(), "log");
30 | }
31 | }
32 |
33 | /**
34 | * 获取Crash日志路径
35 | */
36 | public static File getCrashLogDir(Context context) {
37 | if (isSDCardMounted()) {
38 | return context.getExternalFilesDir("crash");
39 | } else {
40 | return new File(context.getFilesDir(), "crash");
41 | }
42 | }
43 |
44 | /**
45 | * Get sdcard directory
46 | * @return SD card directory
47 | */
48 | public static File getSDCardDir() {
49 | return Environment.getExternalStorageDirectory();
50 | }
51 |
52 | /**
53 | * get sdcard public documents directory
54 | * @return SD card public documents directory
55 | */
56 | public static File getPublicDocumentsDir() {
57 | return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/StringUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils;
2 |
3 | public class StringUtils {
4 |
5 | private StringUtils() {
6 | }
7 |
8 | public static String escape(String str) {
9 | if (str == null)
10 | return null;
11 |
12 | StringBuilder sb = new StringBuilder(str.length() + 2);
13 | sb.append('"');
14 | for (int i = 0; i < str.length(); ++i) {
15 | char c = str.charAt(i);
16 | switch (c) {
17 | case '\t':
18 | sb.append("\\t");
19 | break;
20 | case '\b':
21 | sb.append("\\b");
22 | break;
23 | case '\n':
24 | sb.append("\\n");
25 | break;
26 | case '\r':
27 | sb.append("\\r");
28 | break;
29 | case '\f':
30 | sb.append("\\f");
31 | break;
32 | case '\\':
33 | sb.append("\\\\");
34 | break;
35 | case '\'':
36 | sb.append("\\'");
37 | break;
38 | case '\"':
39 | sb.append("\\\"");
40 | break;
41 | default:
42 | if (c < 32 || c >= 127) {
43 | sb.append(String.format("\\u%04x", (int) c));
44 | } else {
45 | sb.append(c);
46 | }
47 | break;
48 | }
49 | }
50 | sb.append('"');
51 | return sb.toString();
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/README-CN.md:
--------------------------------------------------------------------------------
1 | # SmsCode
2 | 识别短信验证码的App,是 [XposedSmsCode](https://github.com/tianma8023/XposedSmsCode) 的普通(非Xposed)版本。能识别短信中验证码,并将其拷贝至系统剪切板,亦可以自动输入验证码。
3 |
4 | [English Version](/README.md)
5 |
6 | # 应用截图
7 | 


8 |
9 | 可以从 [酷安](https://www.coolapk.com/apk/com.github.tianma8023.smscode) 或者 [releases](https://github.com/tianma8023/SmsCode/releases/) 下载最新的App。
10 |
11 | # 已有功能
12 | - 收到验证码短信后将验证码复制到系统剪贴板
13 | - 收到验证码时显示Toast
14 | - 收到验证码时显示通知
15 | - 自定义验证码短信关键字(正则表达式)
16 | - 自定义验证码匹配规则
17 | - 验证码短信标记为已读(试验性)
18 | - 删除验证码短信(试验性)
19 | - 拦截验证码短信通知(试验性)
20 | - 自动输入验证码
21 | - 主题换肤
22 |
23 | # 注意
24 | - **因为使用到无障碍服务和广播接收者等系统组件,在某些深度定制的ROM上可能不适用(已知如华为的 EMUI)**
25 | - **兼容性:兼容 Android 5.0 及以上(api等级≥21)设备。**
26 | - **遇到什么问题请先看App内的“常见问题”**
27 |
28 | # 更新日志
29 | [更新日志](/LOG-CN.md)
30 |
31 | # 感谢
32 | - [drakeet SmsCodeHelper](https://github.com/drakeet/SmsCodeHelper)
33 | - [zhidao8 SmsCodeHelper](https://github.com/zhidao8/SmsCodeHelper)
34 | - [AndPermission](https://github.com/yanzhenjie/AndPermission)
35 | - [ButterKnife](https://github.com/JakeWharton/butterknife)
36 | - [Material Dialogs](https://github.com/afollestad/material-dialogs)
37 | - [Android Shell](https://github.com/jaredrummler/AndroidShell)
38 | - [logback-android](https://github.com/tony19/logback-android)
39 | - [Bugly](https://bugly.qq.com)
40 | - [EventBus](https://github.com/greenrobot/EventBus)
41 | - [GreenDao](https://github.com/greenrobot/greenDAO)
42 | - [GreenDaoUpgradeHelper](https://github.com/yuweiguocn/GreenDaoUpgradeHelper)
43 | - [Gson](https://github.com/google/gson)
44 | - [BRVAH](https://github.com/CymChad/BaseRecyclerViewAdapterHelper)
45 |
46 | # 协议
47 | 所有的源码均遵循 [GPLv3](https://www.gnu.org/licenses/gpl-3.0.txt) 协议
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/preference/ResetEditPreference.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.preference;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.os.Build;
7 | import android.util.AttributeSet;
8 |
9 | import com.github.tianma8023.smscode.R;
10 |
11 | import androidx.preference.EditTextPreference;
12 |
13 |
14 | public class ResetEditPreference extends EditTextPreference {
15 |
16 | private String mDefaultValue;
17 |
18 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
19 | public ResetEditPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
20 | super(context, attrs, defStyleAttr, defStyleRes);
21 | init();
22 | }
23 |
24 | public ResetEditPreference(Context context, AttributeSet attrs, int defStyleAttr) {
25 | super(context, attrs, defStyleAttr);
26 | init();
27 | }
28 |
29 | public ResetEditPreference(Context context, AttributeSet attrs) {
30 | super(context, attrs);
31 | init();
32 | }
33 |
34 | public ResetEditPreference(Context context) {
35 | super(context);
36 | init();
37 | }
38 |
39 | private void init() {
40 | setNegativeButtonText(R.string.reset);
41 | }
42 |
43 | @Override
44 | public void setDefaultValue(Object value) {
45 | super.setDefaultValue(value);
46 | mDefaultValue = (String) value;
47 | }
48 |
49 | @Override
50 | protected Object onGetDefaultValue(TypedArray a, int index) {
51 | Object result = super.onGetDefaultValue(a, index);
52 | mDefaultValue = (String) result;
53 | return result;
54 | }
55 |
56 | public String getDefaultValue() {
57 | return mDefaultValue;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/event/Event.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.event;
2 |
3 | import com.github.tianma8023.smscode.app.rule.RuleEditFragment;
4 | import com.github.tianma8023.smscode.backup.ExportResult;
5 | import com.github.tianma8023.smscode.entity.SmsCodeRule;
6 |
7 | import java.io.File;
8 |
9 | public class Event {
10 |
11 | private Event() {
12 | }
13 |
14 | /**
15 | * Start to edit codeRule event
16 | */
17 | public static class StartRuleEditEvent {
18 | public SmsCodeRule codeRule;
19 | @RuleEditFragment.RuleEditType
20 | public int type;
21 |
22 | public StartRuleEditEvent(@RuleEditFragment.RuleEditType int type, SmsCodeRule codeRule) {
23 | this.type = type;
24 | this.codeRule = codeRule;
25 | }
26 | }
27 |
28 | /**
29 | * Rule create or update event
30 | */
31 | public static class OnRuleCreateOrUpdate {
32 | public SmsCodeRule codeRule;
33 | @RuleEditFragment.RuleEditType
34 | public int type;
35 |
36 | public OnRuleCreateOrUpdate(@RuleEditFragment.RuleEditType int type, SmsCodeRule rule) {
37 | this.codeRule = rule;
38 | this.type = type;
39 | }
40 | }
41 |
42 | /**
43 | * Save template rule event
44 | */
45 | public static class TemplateSaveEvent {
46 | public boolean success;
47 |
48 | public TemplateSaveEvent(boolean success) {
49 | this.success = success;
50 | }
51 | }
52 |
53 | /**
54 | * Load template rule event
55 | */
56 | public static class TemplateLoadEvent {
57 | public SmsCodeRule template;
58 |
59 | public TemplateLoadEvent(SmsCodeRule template) {
60 | this.template = template;
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/base/back/BackPressEventDispatchHelper.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.base.back;
2 |
3 | import java.util.List;
4 |
5 | import androidx.fragment.app.Fragment;
6 | import androidx.fragment.app.FragmentActivity;
7 | import androidx.fragment.app.FragmentManager;
8 |
9 | public class BackPressEventDispatchHelper {
10 |
11 | public static boolean dispatchBackPressedEvent(FragmentManager fragmentManager) {
12 | List fragments = fragmentManager.getFragments();
13 |
14 | if (fragments == null)
15 | return false;
16 |
17 | for (int i = fragments.size() - 1; i >= 0; i--) {
18 | Fragment fragment = fragments.get(i);
19 |
20 | if (isBackPressedIntercepted(fragment)) {
21 | ((BackPressedListener) fragment).onBackPressed();
22 | return true;
23 | }
24 | }
25 |
26 | if (fragmentManager.getBackStackEntryCount() > 0) {
27 | fragmentManager.popBackStack();
28 | return true;
29 | }
30 | return false;
31 | }
32 |
33 | public static boolean dispatchBackPressedEvent(Fragment fragment) {
34 | return dispatchBackPressedEvent(fragment.getChildFragmentManager());
35 | }
36 |
37 | public static boolean dispatchBackPressedEvent(FragmentActivity fragmentActivity) {
38 | return dispatchBackPressedEvent(fragmentActivity.getSupportFragmentManager());
39 | }
40 |
41 | private static boolean isBackPressedIntercepted(Fragment fragment) {
42 | return fragment != null &&
43 | fragment.isVisible() &&
44 | fragment.getUserVisibleHint() &&
45 | fragment instanceof BackPressedListener &&
46 | ((BackPressedListener) fragment).onInterceptBackPressed();
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/theme/ThemeItem.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.theme;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 | import androidx.annotation.ColorRes;
6 | import androidx.annotation.StringRes;
7 | import androidx.annotation.StyleRes;
8 |
9 | public class ThemeItem implements Parcelable{
10 |
11 | private @StringRes int colorNameRes;
12 | private @ColorRes int colorValueRes;
13 | private @StyleRes int themeRes;
14 |
15 | public ThemeItem(@StringRes int colorNameRes, @ColorRes int colorValueRes, @StyleRes int themeRes) {
16 | this.colorNameRes = colorNameRes;
17 | this.colorValueRes = colorValueRes;
18 | this.themeRes = themeRes;
19 | }
20 |
21 | private ThemeItem(Parcel in) {
22 | colorNameRes = in.readInt();
23 | colorValueRes = in.readInt();
24 | themeRes = in.readInt();
25 | }
26 |
27 | public @StringRes int getColorNameRes() {
28 | return colorNameRes;
29 | }
30 |
31 | public @ColorRes int getColorValueRes() {
32 | return colorValueRes;
33 | }
34 |
35 | public @StyleRes int getThemeRes() {
36 | return themeRes;
37 | }
38 |
39 | @Override
40 | public int describeContents() {
41 | return 0;
42 | }
43 |
44 | @Override
45 | public void writeToParcel(Parcel dest, int flags) {
46 | dest.writeInt(colorNameRes);
47 | dest.writeInt(colorValueRes);
48 | dest.writeInt(themeRes);
49 | }
50 |
51 | public static final Creator CREATOR = new Creator() {
52 | @Override
53 | public ThemeItem createFromParcel(Parcel in) {
54 | return new ThemeItem(in);
55 | }
56 |
57 | @Override
58 | public ThemeItem[] newArray(int size) {
59 | return new ThemeItem[size];
60 | }
61 | };
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/SnackbarHelper.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils;
2 |
3 | import android.view.View;
4 |
5 | import androidx.annotation.NonNull;
6 | import androidx.annotation.StringRes;
7 |
8 | import com.google.android.material.snackbar.Snackbar;
9 |
10 | public class SnackbarHelper {
11 |
12 | private SnackbarHelper() {
13 | }
14 |
15 | @NonNull
16 | public static Snackbar makeShort(@NonNull View view, @StringRes int resId) {
17 | return make(view, resId, Snackbar.LENGTH_SHORT);
18 | }
19 |
20 | @NonNull
21 | public static Snackbar makeShort(@NonNull View view, @NonNull CharSequence text) {
22 | return make(view, text, Snackbar.LENGTH_SHORT);
23 | }
24 |
25 | @NonNull
26 | public static Snackbar makeLong(@NonNull View view, @StringRes int resId) {
27 | return make(view, resId, Snackbar.LENGTH_LONG);
28 | }
29 |
30 | @NonNull
31 | public static Snackbar makeLong(@NonNull View view, @NonNull CharSequence text) {
32 | return make(view, text, Snackbar.LENGTH_LONG);
33 | }
34 |
35 | @NonNull
36 | private static Snackbar make(@NonNull View view, @StringRes int resId, int duration) {
37 | return make(view, view.getResources().getText(resId), duration);
38 | }
39 |
40 | @NonNull
41 | private static Snackbar make(@NonNull View view, @NonNull CharSequence text, int duration) {
42 | // View snackView = snackbar.getView();
43 | // TypedValue typedValue = new TypedValue();
44 | // Context context = view.getContext();
45 | // context.getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true);
46 | // @ColorInt int colorPrimary = typedValue.data;
47 | // colorPrimary = ColorUtils.gradientColor(colorPrimary, 1.2f);
48 | // snackView.setBackgroundColor(colorPrimary);
49 | return Snackbar.make(view, text, duration);
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/XLog.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils;
2 |
3 | import android.content.Context;
4 |
5 | import org.slf4j.LoggerFactory;
6 |
7 | import androidx.annotation.NonNull;
8 | import ch.qos.logback.classic.Level;
9 | import ch.qos.logback.classic.Logger;
10 |
11 | /**
12 | * Log utils based on logback.
13 | */
14 | public class XLog {
15 |
16 | private static Logger sLogger;
17 |
18 | private XLog() {
19 | }
20 |
21 | public static void init(Context context) {
22 | if (sLogger == null) {
23 | initLogger(context);
24 | }
25 | }
26 |
27 | private static void initLogger(Context context) {
28 | sLogger = (Logger) LoggerFactory.getLogger("SmsCodeLogger");
29 | boolean isVerboseLogMode = SPUtils.isVerboseLogMode(context);
30 | if (isVerboseLogMode) {
31 | setLogLevel(Level.TRACE);
32 | } else {
33 | setLogLevel(Level.INFO);
34 | }
35 | }
36 |
37 | public static void v(String message, Object... args) {
38 | sLogger.trace(message, args);
39 | }
40 |
41 | public static void d(String message, Object... args) {
42 | sLogger.debug(message, args);
43 | }
44 |
45 | public static void i(String message, Object... args) {
46 | sLogger.info(message, args);
47 | }
48 |
49 | public static void w(String message, Object... args) {
50 | sLogger.info(message, args);
51 | }
52 |
53 | public static void e(String message, Object... args) {
54 | sLogger.error(message, args);
55 | }
56 |
57 | public static void e(String message, Throwable e) {
58 | sLogger.error(message, e);
59 | }
60 |
61 | public static void setLogLevel(@NonNull Level level) {
62 | Level curLevel = sLogger.getLevel();
63 | if (curLevel.toInt() != level.toInt()) {
64 | sLogger.setLevel(level);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_code_record.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
23 |
24 |
32 |
33 |
41 |
42 |
51 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/shortcuts.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
12 |
14 |
15 |
16 |
21 |
25 |
27 |
28 |
29 |
34 |
38 |
41 |
42 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/migrate/PreferencesTransition.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.migrate;
2 |
3 | import android.content.Context;
4 |
5 | import com.github.tianma8023.smscode.BuildConfig;
6 | import com.github.tianma8023.smscode.R;
7 | import com.github.tianma8023.smscode.utils.SPUtils;
8 | import com.github.tianma8023.smscode.utils.XLog;
9 |
10 | /**
11 | * SharedPreferences 相关数据迁移
12 | */
13 | public class PreferencesTransition implements ITransition {
14 |
15 | private Context mContext;
16 | private int mLocalVersionCode;
17 |
18 | private static final int VERSION_CODE_2 = 2;
19 |
20 | PreferencesTransition(Context context) {
21 | mContext = context;
22 | mLocalVersionCode = SPUtils.getLocalVersionCode(mContext);
23 | }
24 |
25 | @Override
26 | public boolean shouldTransit() {
27 | if (mLocalVersionCode <= VERSION_CODE_2) {
28 | // v1.0.1 及以前版本,需要进行数据兼容
29 | return true;
30 | }
31 | return false;
32 | }
33 |
34 | @Override
35 | public boolean doTransition() {
36 | try {
37 | if (mLocalVersionCode <= VERSION_CODE_2) {
38 | if (SPUtils.isAutoInputRootMode(mContext)) {
39 | // auto-input 模式是 root模式
40 | SPUtils.setAutoInputMode(mContext,
41 | mContext.getString(R.string.auto_input_mode_root));
42 | } else if (SPUtils.isAutoInputAccessibilityMode(mContext)) {
43 | // auto-input 模式是 accessibility模式
44 | SPUtils.setAutoInputMode(mContext,
45 | mContext.getString(R.string.auto_input_mode_accessibility));
46 | }
47 | }
48 | SPUtils.setLocalVersionCode(mContext, BuildConfig.VERSION_CODE);
49 | return true;
50 | } catch (Exception e) {
51 | XLog.e("Error occurs when do preferences transition.", e);
52 | }
53 | return false;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/PackageUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils;
2 |
3 | import android.content.Context;
4 | import android.content.pm.ApplicationInfo;
5 | import android.content.pm.PackageInfo;
6 | import android.content.pm.PackageManager;
7 |
8 | import com.github.tianma8023.smscode.constant.Const;
9 |
10 |
11 | /**
12 | * 包相关工具类
13 | */
14 | public class PackageUtils {
15 |
16 | private PackageUtils() {
17 | }
18 |
19 | public static boolean isAlipayInstalled(Context context) {
20 | return isPackageInstalled(context, Const.ALIPAY_PACKAGE_NAME);
21 | }
22 |
23 | public static boolean isAlipayEnabled(Context context) {
24 | return isPackageEnabled(context, Const.ALIPAY_PACKAGE_NAME);
25 | }
26 |
27 | public static boolean isCoolMarketInstalled(Context context) {
28 | return isPackageInstalled(context, Const.COOL_MARKET_PACKAGE_NAME);
29 | }
30 |
31 | public static boolean isCoolMarketEnabled(Context context) {
32 | return isPackageEnabled(context, Const.COOL_MARKET_PACKAGE_NAME);
33 | }
34 |
35 | /**
36 | * 指定的包名对应的App是否已安装
37 | *
38 | */
39 | public static boolean isPackageInstalled(Context context, String packageName) {
40 | PackageManager pm = context.getPackageManager();
41 | try {
42 | PackageInfo packageInfo = pm.getPackageInfo(packageName, 0);
43 | return packageInfo != null;
44 | } catch (PackageManager.NameNotFoundException e) {
45 | e.printStackTrace();
46 | }
47 | return false;
48 | }
49 |
50 | /**
51 | * 对应包名的应用是否已启用
52 | */
53 | public static boolean isPackageEnabled(Context context, String packageName) {
54 | PackageManager pm = context.getPackageManager();
55 | try {
56 | ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0);
57 | return appInfo != null && appInfo.enabled;
58 | } catch (PackageManager.NameNotFoundException e) {
59 | // ignore
60 | }
61 | return false;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_rules.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #F44336
8 | #F44336
9 | #FF4D3E
10 |
11 | #FB7299
12 | #FB7299
13 | #FF83AF
14 |
15 | #ffb300
16 | #ffb300
17 | #ffc107
18 |
19 | #4CAF50
20 | #4CAF50
21 | #66bb6a
22 |
23 | #00796b
24 | #00796b
25 | #00897b
26 |
27 | #2196F3
28 | #2196F3
29 | #25ACFF
30 |
31 | #9C27B0
32 | #9C27B0
33 | #B32CCA
34 |
35 | #212121
36 | #212121
37 |
38 | #f0f0f0
39 | #6f6f6f
40 |
41 | #202020
42 | #818181
43 |
44 | #FFFFFF
45 | #353535
46 |
47 | #DDDDDD
48 | #454545
49 |
50 |
--------------------------------------------------------------------------------
/LOG-CN.md:
--------------------------------------------------------------------------------
1 | # 更新日志
2 | - 20.01.13 v1.8.2
3 | 1. 修复: Android 10 规则导入导出问题
4 | 2. 变化: Android 10 恢复 "复制到剪切板" 选项
5 | - 19.08.06 v1.8.1
6 | 1. 修复: 非验证码短信到来时始终显示"短信解析中..."的通知的Bug
7 | - 19.08.06 v1.8.0
8 | 1. 初步适配 Android Q,targetSdk 升到 Android Q
9 | 2. 新增: 自动清除验证码通知,并可以指定延迟清理的时间
10 | 3. 变化: 迁移至 androidx
11 | 4. 变化: 更改部分UI细节
12 | 5. 优化: 验证码解析处理逻辑
13 | 6. 修复: 修复部分逻辑错误和显示错误
14 | - 19.03.27 v1.7.3
15 | 1. 优化:识别验证码中有空格的特殊情况
16 | 2. 修复:分享规则文件失效
17 | - 19.03.18 v1.7.2
18 | 1. 新增可选项:显示验证码通知
19 | 2. 优化:验证码识别算法
20 | - 19.03.02 v1.7.1
21 | 1. 新增:酷安评分入口
22 | 2. 优化:部分功能逻辑
23 | - 19.02.04 v1.7.0 过年好~
24 | 1. 优化:验证码识别算法
25 | - 19.01.21 v1.6.5
26 | 1. 新增:忽略电池优化入口
27 | 2. 移除:友盟统计
28 | 3. 优化:适配导航栏颜色
29 | 4. 其他Bug修复
30 | - 19.01.05 v1.6.4
31 | 1. 部分UI美化调整
32 | 2. 规范化应用:规则备份导出至/sdcard/Documents/
33 | - 19.01.04 v1.6.3
34 | 1. 更好地适配暗色主题
35 | 2. 新增领取支付宝红包入口
36 | 3. 优化自动输入模式选择流程和捐赠流程
37 | 4. 修正部分文案
38 | - 18.12.26 v1.6.2 the delayed Merry X'mas
39 | 1. 优化:验证码记录可以查看短信详情
40 | 2. 改动:App默认不在最近任务列表中隐藏
41 | 3. 其他 Bug 修复
42 | - 18.11.29 v1.6.1
43 | 1. Bug fix:验证码记录公司名解析问题
44 | - 18.11.21 v1.6.0
45 | 1. 新增实验性选项:拦截验证码短信通知
46 | 2. 新增主题色:水鸭青
47 | - 18.11.17 v1.5.0
48 | 1. 添加可选项:短信验证码历史记录
49 | 2. 添加 快捷路径(Shortcuts)
50 | - 18.10.30 v1.4.2
51 | 1. 自动输入时的异常崩溃修复
52 | - 18.10.27 v1.4.1
53 | 1. 使用官方推荐的首选项组件以修复部分UI问题
54 | - 18.10.26 v1.4.0
55 | 1. 提升targetSDK到P,更新项目构建工具,并不再支持4.4系统
56 | 2. 添加各种图标
57 | 3. 修复部分机型无法删除短信的Bug
58 | 4. 移除“默认蓝”,将“胖次蓝”作为默认主题色
59 | - 18.10.23 v1.3.2
60 | 1. 新增可选项:不在最近任务列表中显示
61 | 2. 新增可选项:提取成功后删除验证码短信
62 | 3. 新增可选项:提取成功后复制验证码到剪切板
63 | 4. 新增可选项:自动对焦失效后转为手动对焦
64 | 5. 新增更多人性化设置提示
65 | 6. Bug 修复
66 | - 18.10.03 v1.3.1
67 | 1. 优化自动生成的短信验证码规则
68 | 2. 自定义验证码规则中的公司名和关键字是忽略大小写的
69 | 3. bug修复
70 | - 18.09.30 v1.3.0
71 | 1. 新增:支持自定义验证码规则
72 | 2. 标记为已读移至实验性选项中
73 | - 18.09.19 v1.2.0
74 | 1. 新增选项:标记验证码短信为已读
75 | - 18.09.16 v1.1.1
76 | 1. 将“手动对焦”设置为默认对焦方式
77 | 2. 增强在webview中自动输入的能力
78 | 3. 默认验证码关键字新增“动态口令”
79 | - 18.09.13 v1.1.0
80 | 1. 将 “无障碍服务模式” 和 “Root模式” 合并在 “自动输入模式” 中
81 | 2. 增加新选项:自动输入成功后清除剪切板内容
82 | 3. 潜在性的异常修复以及其他BUG修复
83 | - 18.09.11 v1.0.1
84 | 1. Bug fix
85 | 2. 添加版本信息的显示
86 | 3. 更换"调试"模块的位置
87 | - 18.09.07 v1.0.0
88 | 1. 移植Xposed版本的绝大部分功能
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/service/CopyCodeService.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.service;
2 |
3 | import android.app.Service;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.os.Build;
7 | import android.os.IBinder;
8 | import android.widget.Toast;
9 |
10 | import com.github.tianma8023.smscode.BuildConfig;
11 | import com.github.tianma8023.smscode.R;
12 | import com.github.tianma8023.smscode.utils.ClipboardUtils;
13 |
14 | /**
15 | * Code copy service
16 | */
17 | public class CopyCodeService extends Service {
18 |
19 | private static final String ACTION_COPY_CODE = BuildConfig.APPLICATION_ID + ".action.COPY_CODE";
20 | private static final String EXTRA_KEY_CODE = "key_code";
21 |
22 | public CopyCodeService() {
23 | }
24 |
25 | public static Intent createCopyCodeIntent(Context context, String smsCode) {
26 | Intent intent = new Intent(context, CopyCodeService.class);
27 | intent.setAction(ACTION_COPY_CODE);
28 | intent.putExtra(EXTRA_KEY_CODE, smsCode);
29 | return intent;
30 | }
31 |
32 | @Override
33 | public IBinder onBind(Intent intent) {
34 | return null;
35 | }
36 |
37 | @Override
38 | public int onStartCommand(Intent intent, int flags, int startId) {
39 | if (intent != null) {
40 | final String action = intent.getAction();
41 | if (ACTION_COPY_CODE.equals(action)) {
42 | final String smsCode = intent.getStringExtra(EXTRA_KEY_CODE);
43 | handleActionCopyCode(smsCode);
44 | sendStopHandleServiceBroadcast(this);
45 | }
46 | }
47 | stopSelf();
48 | return START_NOT_STICKY;
49 | }
50 |
51 | private void handleActionCopyCode(String smsCode) {
52 | ClipboardUtils.copyToClipboard(this, smsCode);
53 |
54 | String content = getString(R.string.prompt_sms_code_copied, smsCode);
55 | Toast.makeText(this, content, Toast.LENGTH_LONG).show();
56 | }
57 |
58 | private void sendStopHandleServiceBroadcast(Context context) {
59 | Intent intent = new Intent();
60 | intent.setAction(SmsCodeHandleService.ACTION_STOP_HANDLE_SERVICE);
61 | context.sendBroadcast(intent);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/receiver/SmsReceiver.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.receiver;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.os.Bundle;
7 | import android.provider.Telephony;
8 | import android.telephony.SmsMessage;
9 |
10 | import com.github.tianma8023.smscode.entity.SmsMsg;
11 | import com.github.tianma8023.smscode.service.SmsCodeHandleService;
12 | import com.github.tianma8023.smscode.utils.SmsMessageUtils;
13 | import com.github.tianma8023.smscode.utils.XLog;
14 |
15 | import androidx.core.content.ContextCompat;
16 |
17 | public class SmsReceiver extends BroadcastReceiver {
18 |
19 | private static final String SMS_RECEIVED = Telephony.Sms.Intents.SMS_RECEIVED_ACTION;
20 |
21 | @Override
22 | public void onReceive(Context context, Intent intent) {
23 | XLog.d("SmsReceiver#onReceived() - {}", intent.getAction());
24 | if (SMS_RECEIVED.equals(intent.getAction())) {
25 | Bundle bundle = intent.getExtras();
26 | if (bundle != null) {
27 | Object[] pdus = (Object[]) bundle.get("pdus");
28 | if (pdus == null)
29 | return;
30 |
31 | final SmsMessage[] messages = new SmsMessage[pdus.length];
32 | for (int i = 0; i < pdus.length; i++) {
33 | messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
34 | }
35 |
36 | if (messages.length != 0) {
37 | String body = SmsMessageUtils.getMessageBody(messages);
38 | String sender = messages[0].getOriginatingAddress();
39 | long date = System.currentTimeMillis();
40 |
41 | SmsMsg smsMsg = new SmsMsg();
42 | smsMsg.setBody(body);
43 | smsMsg.setSender(sender);
44 | smsMsg.setDate(date);
45 |
46 | Intent smsCodeHandleSvc = new Intent(context, SmsCodeHandleService.class);
47 | smsCodeHandleSvc.putExtra(SmsCodeHandleService.EXTRA_KEY_SMS_MESSAGE_DATA, smsMsg);
48 | ContextCompat.startForegroundService(context, smsCodeHandleSvc);
49 | }
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/record/CodeRecordsActivity.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.record;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.view.MenuItem;
7 |
8 | import com.github.tianma8023.smscode.R;
9 | import com.github.tianma8023.smscode.app.base.BaseActivity;
10 | import com.github.tianma8023.smscode.app.base.back.BackPressEventDispatchHelper;
11 |
12 | import androidx.appcompat.app.ActionBar;
13 | import androidx.appcompat.widget.Toolbar;
14 | import butterknife.BindView;
15 | import butterknife.ButterKnife;
16 |
17 | /**
18 | * Sms Code Records
19 | */
20 | public class CodeRecordsActivity extends BaseActivity {
21 |
22 | @BindView(R.id.toolbar)
23 | Toolbar mToolbar;
24 |
25 | public static void startToMe(Context context) {
26 | Intent intent = new Intent(context, CodeRecordsActivity.class);
27 | context.startActivity(intent);
28 | }
29 |
30 | @Override
31 | protected void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_code_records);
34 | ButterKnife.bind(this);
35 |
36 | setupToolbar();
37 |
38 | getSupportFragmentManager()
39 | .beginTransaction()
40 | .replace(R.id.code_records_main_content, CodeRecordsFragment.newInstance())
41 | .commit();
42 | }
43 |
44 | private void setupToolbar() {
45 | setSupportActionBar(mToolbar);
46 | ActionBar actionBar = getSupportActionBar();
47 | if (actionBar != null) {
48 | actionBar.setHomeButtonEnabled(true);
49 | actionBar.setDisplayHomeAsUpEnabled(true);
50 | }
51 | }
52 |
53 | @Override
54 | public boolean onOptionsItemSelected(MenuItem item) {
55 | switch (item.getItemId()) {
56 | case android.R.id.home:
57 | onBackPressed();
58 | break;
59 | default:
60 | return super.onOptionsItemSelected(item);
61 | }
62 | return true;
63 | }
64 |
65 | @Override
66 | public void onBackPressed() {
67 | if (!BackPressEventDispatchHelper.dispatchBackPressedEvent(this)) {
68 | super.onBackPressed();
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SmsCode
2 | SmsCode is non-xposed version of [XposedSmsCode](https://github.com/tianma8023/XposedSmsCode)。 It can recognize, parse SMS code and copy it to clipboard when a new message arrives, it can also auto-input SMS code.
3 |
4 | [中文版说明](/README-CN.md)
5 |
6 | # Screenshots
7 | 


8 |
9 | You can download the latest App from [Coolapk](https://www.coolapk.com/apk/com.github.tianma8023.smscode) or [releases](https://github.com/tianma8023/SmsCode/releases/).
10 |
11 | # Attention
12 | - **This module is suitable for AOSP ROM, it may not work well on other 3rd-party Rom.**
13 | - **Compatibility: Requires Android 5.0+ (api level ≥ 21).**
14 | - **Read the FAQ in app first if you encountered any problems.**
15 |
16 | # Features
17 | - Copy verification code to clipboard when a new message arrives.
18 | - Show toast when a SMS verification code is copied.
19 | - Show notification when code SMS parsed.
20 | - Custom keywords about verification code message (regular expressions allowed).
21 | - Custom SMS code match rules.
22 | - Mark verification code SMS as read (experimental).
23 | - Delete verification SMS when it been extracted successfully (experimental).
24 | - Block code SMS message notification (experimental).
25 | - Auto-input SMS code.
26 | - Various theme color to choose.
27 |
28 | # Update Logs
29 | [Update logs](/LOG-EN.md)
30 |
31 | # Thanks
32 | - [drakeet SmsCodeHelper](https://github.com/drakeet/SmsCodeHelper)
33 | - [zhidao8 SmsCodeHelper](https://github.com/zhidao8/SmsCodeHelper)
34 | - [AndPermission](https://github.com/yanzhenjie/AndPermission)
35 | - [ButterKnife](https://github.com/JakeWharton/butterknife)
36 | - [Material Dialogs](https://github.com/afollestad/material-dialogs)
37 | - [Android Shell](https://github.com/jaredrummler/AndroidShell)
38 | - [logback-android](https://github.com/tony19/logback-android)
39 | - [Bugly](https://bugly.qq.com)
40 | - [EventBus](https://github.com/greenrobot/EventBus)
41 | - [GreenDao](https://github.com/greenrobot/greenDAO)
42 | - [GreenDaoUpgradeHelper](https://github.com/yuweiguocn/GreenDaoUpgradeHelper)
43 | - [Gson](https://github.com/google/gson)
44 | - [BRVAH](https://github.com/CymChad/BaseRecyclerViewAdapterHelper)
45 |
46 | # License
47 | All code is licensed under [GPLv3](https://www.gnu.org/licenses/gpl-3.0.txt)
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/PreferenceUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 |
6 | import com.github.tianma8023.smscode.constant.PrefConst;
7 |
8 | /**
9 | * Common Shared preferences utils.
10 | */
11 | public class PreferenceUtils {
12 |
13 | private PreferenceUtils() {
14 |
15 | }
16 |
17 | private static SharedPreferences getPreferences(Context context) {
18 | return context.getSharedPreferences(PrefConst.REMOTE_PREF_NAME,
19 | Context.MODE_PRIVATE);
20 | }
21 |
22 | public static boolean contains(Context context, String key) {
23 | return getPreferences(context).contains(key);
24 | }
25 |
26 | public static String getString(Context context, String key, String defValue) {
27 | return getPreferences(context).getString(key, defValue);
28 | }
29 |
30 | public static void putString(Context context, String key, String value) {
31 | getPreferences(context).edit().putString(key, value).apply();
32 | }
33 |
34 | public static boolean getBoolean(Context context, String key, boolean defValue) {
35 | return getPreferences(context).getBoolean(key, defValue);
36 | }
37 |
38 | public static void putBoolean(Context context, String key, boolean value) {
39 | getPreferences(context).edit().putBoolean(key, value).apply();
40 | }
41 |
42 | public static int getInt(Context context, String key, int defValue) {
43 | return getPreferences(context).getInt(key, defValue);
44 | }
45 |
46 | public static void putInt(Context context, String key, int value) {
47 | getPreferences(context).edit().putInt(key, value).apply();
48 | }
49 |
50 | public static float getFloat(Context context, String key, float defValue) {
51 | return getPreferences(context).getFloat(key, defValue);
52 | }
53 |
54 | public static void putFloat(Context context, String key, float value) {
55 | getPreferences(context).edit().putFloat(key, value).apply();
56 | }
57 |
58 | public static long getLong(Context context, String key, long defValue) {
59 | return getPreferences(context).getLong(key, defValue);
60 | }
61 |
62 | public static void putLong(Context context, String key, long value) {
63 | getPreferences(context).edit().putLong(key, value).apply();
64 | }
65 |
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/AppOpsUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils;
2 |
3 | import android.app.AppOpsManager;
4 | import android.content.Context;
5 | import android.os.Process;
6 |
7 | import java.lang.reflect.Method;
8 |
9 | /**
10 | * AppOpsManager utils
11 | */
12 | public final class AppOpsUtils {
13 | public static final int OP_WRITE_SMS = 15;
14 |
15 | private static final Method sCheckOpMethod;
16 | private static final Method sNoteOpMethod;
17 | private static final Method sSetModeMethod;
18 |
19 | static {
20 | Class cls = AppOpsManager.class;
21 | sCheckOpMethod = ReflectionUtils.getDeclaredMethod(cls, "checkOpNoThrow", int.class, int.class, String.class);
22 | sNoteOpMethod = ReflectionUtils.getDeclaredMethod(cls, "noteOpNoThrow", int.class, int.class, String.class);
23 | sSetModeMethod = ReflectionUtils.getDeclaredMethod(cls, "setMode", int.class, int.class, String.class, int.class);
24 | }
25 |
26 | private AppOpsUtils() { }
27 |
28 | private static AppOpsManager getAppOpsManager(Context context) {
29 | return (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
30 | }
31 |
32 | public static boolean checkOp(Context context, int opCode, int uid, String packageName) {
33 | AppOpsManager appOpsManager = getAppOpsManager(context);
34 | int result = (Integer)ReflectionUtils.invoke(sCheckOpMethod, appOpsManager, opCode, uid, packageName);
35 | return result == AppOpsManager.MODE_ALLOWED;
36 | }
37 |
38 | public static boolean noteOp(Context context, int opCode, int uid, String packageName) {
39 | AppOpsManager appOpsManager = getAppOpsManager(context);
40 | int result = (Integer)ReflectionUtils.invoke(sNoteOpMethod, appOpsManager, opCode, uid, packageName);
41 | return result == AppOpsManager.MODE_ALLOWED;
42 | }
43 |
44 | public static boolean noteOp(Context context, int opCode) {
45 | int uid = Process.myUid();
46 | String packageName = context.getPackageName();
47 | return noteOp(context, opCode, uid, packageName);
48 | }
49 |
50 | public static void allowOp(Context context, int opCode, int uid, String packageName) {
51 | AppOpsManager appOpsManager = getAppOpsManager(context);
52 | ReflectionUtils.invoke(sSetModeMethod, appOpsManager, opCode, uid, packageName, AppOpsManager.MODE_ALLOWED);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/settings_auto_input_code.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
14 |
15 |
23 |
24 |
32 |
33 |
40 |
41 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/widget/FabScrollBehavior.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.view.animation.LinearInterpolator;
7 |
8 | import com.google.android.material.floatingactionbutton.FloatingActionButton;
9 |
10 | import androidx.annotation.NonNull;
11 | import androidx.coordinatorlayout.widget.CoordinatorLayout;
12 | import androidx.core.view.ViewCompat;
13 |
14 | /**
15 | * Floating Action Bar scroll behavior
16 | */
17 | public class FabScrollBehavior extends FloatingActionButton.Behavior {
18 |
19 | public FabScrollBehavior() {
20 | }
21 |
22 | public FabScrollBehavior(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | }
25 |
26 | @Override
27 | public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
28 | @NonNull FloatingActionButton child,
29 | @NonNull View directTargetChild,
30 | @NonNull View target, int axes, int type) {
31 | // Ensure we react to vertical scrolling
32 | return type == ViewCompat.TYPE_TOUCH && axes == ViewCompat.SCROLL_AXIS_VERTICAL;
33 | }
34 |
35 | @Override
36 | public boolean onNestedFling(@NonNull CoordinatorLayout coordinatorLayout,
37 | @NonNull FloatingActionButton child,
38 | @NonNull View target, float velocityX,
39 | float velocityY, boolean consumed) {
40 | if (velocityY > 500) {
41 | animateOut(child);
42 | return true;
43 | } else if (velocityY < -500) {
44 | animateIn(child);
45 | return true;
46 | }
47 | return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
48 | }
49 |
50 | private void animateOut(FloatingActionButton fab) {
51 | CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
52 | int bottomMargin = layoutParams.bottomMargin;
53 | fab.animate().translationY(fab.getHeight() + bottomMargin).setInterpolator(new LinearInterpolator()).start();
54 | }
55 |
56 | private void animateIn(FloatingActionButton fab) {
57 | fab.animate().translationY(0).setInterpolator(new LinearInterpolator()).start();
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/app/and-res-guard.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'AndResGuard'
2 |
3 | andResGuard {
4 | // mappingFile = file("./resource_mapping.txt")
5 | mappingFile = null
6 | use7zip = true
7 | useSign = true
8 | // 打开这个开关,会keep住所有资源的原始路径,只混淆资源的名字
9 | keepRoot = false
10 | whiteList = [
11 | // for your icon
12 | "R.mipmap.ic_launcher",
13 | "R.mipmap.ic_launcher_round",
14 |
15 | // Umeng sdk
16 | "R.anim.umeng*",
17 | "R.string.umeng*",
18 | "R.string.UM*",
19 | "R.string.tb_*",
20 | "R.layout.umeng*",
21 | "R.layout.socialize_*",
22 | "R.layout.*messager*",
23 | "R.layout.tb_*",
24 | "R.color.umeng*",
25 | "R.color.tb_*",
26 | "R.style.*UM*",
27 | "R.style.umeng*",
28 | "R.drawable.umeng*",
29 | "R.drawable.tb_*",
30 | "R.drawable.sina*",
31 | "R.drawable.qq_*",
32 | "R.drawable.tb_*",
33 | "R.id.umeng*",
34 | "R.id.*messager*",
35 | "R.id.progress_bar_parent",
36 | "R.id.socialize_*",
37 | "R.id.webView",
38 |
39 | // Google service
40 | "R.string.google_app_id",
41 | "R.string.gcm_defaultSenderId",
42 | "R.string.default_web_client_id",
43 | "R.string.ga_trackingId",
44 | "R.string.firebase_database_url",
45 | "R.string.google_api_key",
46 | "R.string.google_crash_reporting_api_key",
47 |
48 | // getui
49 | "R.drawable.push",
50 | "R.drawable.push_small",
51 | "R.layout.getui_notification",
52 |
53 | // JPush
54 | "R.drawable.jpush_notification_icon",
55 |
56 | // GrowingIO
57 | "R.string.growingio_project_id",
58 | "R.string.growingio_url_scheme",
59 | "R.string.growingio_channel",
60 | ]
61 | compressFilePattern = [
62 | "*.png",
63 | "*.jpg",
64 | "*.jpeg",
65 | "*.gif",
66 | ]
67 | sevenzip {
68 | artifact = 'com.tencent.mm:SevenZip:1.2.16'
69 | //path = "/usr/local/bin/7za"
70 | }
71 |
72 | /**
73 | * 可选: 如果不设置则会默认覆盖assemble输出的apk
74 | **/
75 | // finalApkBackupPath = "${project.rootDir}/final.apk"
76 |
77 | /**
78 | * 可选: 指定v1签名时生成jar文件的摘要算法
79 | * 默认值为“SHA-1”
80 | **/
81 | // digestalg = "SHA-256"
82 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_automatic.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
19 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/backup/RuleExporter.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.backup;
2 |
3 | import com.github.tianma8023.smscode.entity.SmsCodeRule;
4 | import com.google.gson.stream.JsonWriter;
5 |
6 | import java.io.Closeable;
7 | import java.io.File;
8 | import java.io.FileNotFoundException;
9 | import java.io.FileOutputStream;
10 | import java.io.IOException;
11 | import java.io.OutputStream;
12 | import java.io.OutputStreamWriter;
13 | import java.nio.charset.StandardCharsets;
14 | import java.util.List;
15 |
16 | /**
17 | * SmsCode rules exporter
18 | */
19 | public class RuleExporter implements Closeable {
20 |
21 | private JsonWriter mJsonWriter;
22 |
23 | public RuleExporter(OutputStream out) {
24 | OutputStreamWriter osw;
25 | osw = new OutputStreamWriter(out, StandardCharsets.UTF_8);
26 | mJsonWriter = new JsonWriter(osw);
27 | mJsonWriter.setIndent("\t"); // pretty print
28 | }
29 |
30 | public RuleExporter(File file) throws FileNotFoundException {
31 | this(new FileOutputStream(file));
32 | }
33 |
34 | public void doExport(List ruleList) throws IOException {
35 | begin();
36 | exportRuleList(ruleList);
37 | end();
38 | }
39 |
40 | private void begin() throws IOException {
41 | mJsonWriter.beginObject();
42 | mJsonWriter.name(BackupConst.KEY_VERSION)
43 | .value(BackupConst.BACKUP_VERSION);
44 | }
45 |
46 | private void exportRuleList(List ruleList) throws IOException {
47 | mJsonWriter.name(BackupConst.KEY_RULES)
48 | .beginArray();
49 | for (SmsCodeRule rule : ruleList) {
50 | exportRule(rule);
51 | }
52 | mJsonWriter.endArray();
53 | }
54 |
55 | private void exportRule(SmsCodeRule rule) throws IOException {
56 | mJsonWriter.beginObject();
57 | mJsonWriter.name(BackupConst.KEY_COMPANY).value(rule.getCompany());
58 | mJsonWriter.name(BackupConst.KEY_CODE_KEYWORD).value(rule.getCodeKeyword());
59 | mJsonWriter.name(BackupConst.KEY_CODE_REGEX).value(rule.getCodeRegex());
60 | mJsonWriter.endObject();
61 | }
62 |
63 | private void end() throws IOException {
64 | mJsonWriter.endObject();
65 | }
66 |
67 | @Override
68 | public void close() {
69 | if (mJsonWriter != null) {
70 | try {
71 | mJsonWriter.close();
72 | } catch (IOException e) {
73 | e.printStackTrace();
74 | }
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/Utils.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils;
2 |
3 | import android.content.Context;
4 | import android.net.Uri;
5 | import android.widget.Toast;
6 |
7 | import com.github.tianma8023.smscode.R;
8 |
9 | import java.util.Locale;
10 |
11 | import androidx.browser.customtabs.CustomTabsIntent;
12 |
13 | /**
14 | * Other Utils
15 | */
16 | public class Utils {
17 |
18 | private Utils() {
19 | }
20 |
21 | public static void showWebPage(Context context, String url) {
22 | try {
23 | CustomTabsIntent cti = new CustomTabsIntent.Builder().build();
24 | cti.launchUrl(context, Uri.parse(url));
25 | } catch (Exception e) {
26 | Toast.makeText(context, R.string.browser_install_or_enable_prompt, Toast.LENGTH_SHORT).show();
27 | }
28 | }
29 |
30 | private static String getLanguagePath() {
31 | Locale locale = Locale.getDefault();
32 | String language = locale.getLanguage();
33 | String country = locale.getCountry();
34 | String result = "en";
35 | if ("zh".equals(language)) {
36 | if ("CN".equalsIgnoreCase(country)) {
37 | result = "zh-CN";
38 | } else if ("HK".equalsIgnoreCase(country) || "TW".equalsIgnoreCase(country)) {
39 | result = "zh-TW";
40 | }
41 | }
42 | return result;
43 | }
44 |
45 | public static String getProjectDocUrl(String docBaseUrl, String docPath) {
46 | return docBaseUrl + "/" + getLanguagePath() + "/" + docPath;
47 | }
48 |
49 | public static boolean isValidFilename(String filename) {
50 | if (filename == null || filename.trim().length() == 0) {
51 | return false;
52 | }
53 |
54 | if (".".equals(filename.trim()) || "..".equals(filename.trim())) {
55 | return false;
56 | }
57 |
58 | for(int i = 0; i < filename.length(); i++) {
59 | char c = filename.charAt(i);
60 | if (!isValidFilenameChar(c)) {
61 | return false;
62 | }
63 | }
64 | return true;
65 | }
66 |
67 | private static boolean isValidFilenameChar(char c) {
68 | // check control characters
69 | if (c <= 1f || c == 7f) {
70 | return false;
71 | }
72 |
73 | // check special characters
74 | switch (c) {
75 | case '"':
76 | case '*':
77 | case '/':
78 | case '\\':
79 | case '<':
80 | case '>':
81 | case '|':
82 | case '?':
83 | case ',':
84 | case ';':
85 | case ':':
86 | return false;
87 | }
88 |
89 | return true;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/rule/RuleAdapter.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.rule;
2 |
3 | import android.view.ContextMenu;
4 | import android.view.View;
5 |
6 | import com.chad.library.adapter.base.BaseQuickAdapter;
7 | import com.chad.library.adapter.base.BaseViewHolder;
8 | import com.github.tianma8023.smscode.R;
9 | import com.github.tianma8023.smscode.adapter.OnCreateItemContextMenuListener;
10 | import com.github.tianma8023.smscode.entity.SmsCodeRule;
11 |
12 | import java.util.List;
13 |
14 | public class RuleAdapter extends BaseQuickAdapter {
15 |
16 | private OnCreateItemContextMenuListener mContextMenuListener;
17 |
18 | RuleAdapter(List ruleList) {
19 | super(R.layout.item_rule, ruleList);
20 | }
21 |
22 | @Override
23 | protected void convert(BaseViewHolder helper, SmsCodeRule item) {
24 | helper.setText(R.id.rule_company_text_view, item.getCompany())
25 | .setText(R.id.rule_keyword_text_view, item.getCodeKeyword())
26 | .setText(R.id.rule_regex_text_view, item.getCodeRegex());
27 | }
28 |
29 | public void setContextMenuListener(OnCreateItemContextMenuListener contextMenuListener) {
30 | mContextMenuListener = contextMenuListener;
31 | }
32 |
33 | @Override
34 | public void onBindViewHolder(BaseViewHolder holder, int position) {
35 | super.onBindViewHolder(holder, position);
36 |
37 | final int pos = position;
38 | holder.itemView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
39 | @Override
40 | public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
41 | if (mContextMenuListener != null) {
42 | mContextMenuListener.onCreateItemContextMenu(menu, v, menuInfo, pos);
43 | }
44 | }
45 | });
46 | }
47 |
48 | public void addRule(SmsCodeRule newRule) {
49 | if (!getData().contains(newRule)) {
50 | addData(newRule);
51 | }
52 | }
53 |
54 | public void addRule(List ruleList) {
55 | addData(ruleList);
56 | }
57 |
58 | public void addRule(int position, SmsCodeRule newRule) {
59 | if (!getData().contains(newRule)) {
60 | addData(position, newRule);
61 | }
62 | }
63 |
64 | public void updateAt(int position, SmsCodeRule updatedRule) {
65 | SmsCodeRule item = getItem(position);
66 | if (item != null) {
67 | item.copyFrom(updatedRule);
68 | notifyDataSetChanged();
69 | }
70 | }
71 |
72 | public void removeItemAt(int position) {
73 | remove(position);
74 | }
75 |
76 | public List getRuleList() {
77 | return getData();
78 | }
79 |
80 | public void setRules(List ruleList) {
81 | setNewData(ruleList);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/rom/MiuiUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils.rom;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.content.pm.PackageManager;
6 | import android.content.pm.ResolveInfo;
7 | import android.net.Uri;
8 | import android.provider.Settings;
9 | import android.text.TextUtils;
10 |
11 | import com.github.tianma8023.smscode.utils.XLog;
12 |
13 | import java.util.List;
14 |
15 | /**
16 | * Utils for MIUI.
17 | */
18 | public class MiuiUtils {
19 |
20 | public static final String KEY_VERSION_NAME_MIUI = "ro.miui.ui.version.name";
21 |
22 | private MiuiUtils() {
23 |
24 | }
25 |
26 | /**
27 | * 获取MIUI版本,失败则返回-1
28 | *
29 | * @return miui version code, return -1 if failed.
30 | */
31 | private static int getMiuiVersion() {
32 | String version = RomUtils.getSystemProperty(KEY_VERSION_NAME_MIUI);
33 | if (!TextUtils.isEmpty(version)) {
34 | try {
35 | return Integer.parseInt(version.substring(1));
36 | } catch (Exception e) {
37 | e.printStackTrace();
38 | }
39 | }
40 | return -1;
41 | }
42 |
43 | private static boolean isActivityIntentValid(Context context, Intent intent) {
44 | if (intent == null) {
45 | return false;
46 | }
47 | List matchedActivities = context.getPackageManager().
48 | queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
49 | return matchedActivities != null && !matchedActivities.isEmpty();
50 | }
51 |
52 | public static void goToPermissionEditorActivity(Context context) {
53 | int miuiVer = getMiuiVersion();
54 | Intent intent;
55 | if (miuiVer >= 8) { // miui v8, 9, 10
56 | intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
57 | intent.setClassName("com.miui.securitycenter",
58 | "com.miui.permcenter.permissions.PermissionsEditorActivity");
59 | intent.putExtra("extra_pkgname", context.getPackageName());
60 | } else if (miuiVer >= 6) { // miui v6,7
61 | intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
62 | intent.setClassName("com.miui.securitycenter",
63 | "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
64 | intent.putExtra("extra_pkgname", context.getPackageName());
65 | } else { // other version, 应用信息界面
66 | String packageName = context.getPackageName();
67 | intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
68 | Uri uri = Uri.fromParts("package" , packageName, null);
69 | intent.setData(uri);
70 | }
71 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
72 | if (isActivityIntentValid(context, intent)) {
73 | context.startActivity(intent);
74 | } else {
75 | XLog.e("Intent is invalid {}", intent);
76 | }
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/theme/ThemeItemContainer.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.theme;
2 |
3 | import com.github.tianma8023.smscode.R;
4 | import com.github.tianma8023.smscode.constant.PrefConst;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | import androidx.annotation.ColorRes;
10 | import androidx.annotation.StringRes;
11 | import androidx.annotation.StyleRes;
12 |
13 | public class ThemeItemContainer {
14 |
15 | private List mThemeItemList;
16 |
17 | private static ThemeItemContainer sInstance;
18 |
19 | private ThemeItemContainer() {
20 | mThemeItemList = loadThemeItems();
21 | }
22 |
23 | public static ThemeItemContainer get() {
24 | if (sInstance == null) {
25 | synchronized (ThemeItemContainer.class) {
26 | if (sInstance == null) {
27 | sInstance = new ThemeItemContainer();
28 | }
29 | }
30 | }
31 | return sInstance;
32 | }
33 |
34 | private List loadThemeItems() {
35 | List themeItems = new ArrayList<>();
36 | @StringRes int[] colorNameResArray = {
37 | // R.string.color_default,
38 | R.string.blue,
39 | R.string.red,
40 | R.string.pink,
41 | R.string.yellow,
42 | R.string.teal,
43 | R.string.green,
44 | R.string.violet,
45 | R.string.black,
46 | };
47 | @ColorRes int[] colorValueResArray = {
48 | // R.color.colorPrimaryDark,
49 | R.color.colorPrimaryDark_blue,
50 | R.color.colorPrimaryDark_red,
51 | R.color.colorPrimaryDark_pink,
52 | R.color.colorPrimaryDark_yellow,
53 | R.color.colorPrimaryDark_teal,
54 | R.color.colorPrimaryDark_green,
55 | R.color.colorPrimary_violet,
56 | R.color.colorPrimary_black,
57 | };
58 | @StyleRes int[] themeResArray = {
59 | // R.style.AppTheme,
60 | R.style.AppTheme_Blue,
61 | R.style.AppTheme_Red,
62 | R.style.AppTheme_Pink,
63 | R.style.AppTheme_Yellow,
64 | R.style.AppTheme_Teal,
65 | R.style.AppTheme_Green,
66 | R.style.AppTheme_Violet,
67 | R.style.AppTheme_Black,
68 | };
69 |
70 | for (int i = 0; i < colorNameResArray.length; i++) {
71 | themeItems.add(new ThemeItem(
72 | colorNameResArray[i],
73 | colorValueResArray[i],
74 | themeResArray[i]
75 | ));
76 | }
77 | return themeItems;
78 | }
79 |
80 | public ThemeItem getItemAt(int index) {
81 | // check current theme index in case of exception.
82 | if(index < 0 || index >= mThemeItemList.size()) {
83 | index = PrefConst.CURRENT_THEME_INDEX_DEFAULT;
84 | }
85 | return mThemeItemList.get(index);
86 | }
87 |
88 | public List getThemeItemList() {
89 | return mThemeItemList;
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/ReflectionUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils;
2 |
3 | import java.lang.reflect.Field;
4 | import java.lang.reflect.InvocationTargetException;
5 | import java.lang.reflect.Method;
6 |
7 | /**
8 | * Utils for reflection
9 | */
10 | public final class ReflectionUtils {
11 | private ReflectionUtils() { }
12 |
13 | public static Class> getClass(ClassLoader classLoader, String name) {
14 | try {
15 | return Class.forName(name, true, classLoader);
16 | } catch (ClassNotFoundException e) {
17 | throw new RuntimeException(e);
18 | }
19 | }
20 |
21 | public static Field getDeclaredField(Class> cls, String fieldName) {
22 | Field field;
23 | try {
24 | field = cls.getDeclaredField(fieldName);
25 | } catch (NoSuchFieldException e) {
26 | throw new RuntimeException(e);
27 | }
28 | field.setAccessible(true);
29 | return field;
30 | }
31 |
32 | public static Field getField(Class> cls, String fieldName) {
33 | Field field;
34 | try {
35 | field = cls.getField(fieldName);
36 | } catch (NoSuchFieldException e) {
37 | throw new RuntimeException(e);
38 | }
39 | field.setAccessible(true);
40 | return field;
41 | }
42 |
43 | public static Object getFieldValue(Field field, Object object) {
44 | try {
45 | return field.get(object);
46 | } catch (IllegalAccessException e) {
47 | throw new RuntimeException(e);
48 | }
49 | }
50 |
51 | public static void setFieldValue(Field field, Object object, Object value) {
52 | try {
53 | field.set(object, value);
54 | } catch (IllegalAccessException e) {
55 | throw new RuntimeException(e);
56 | }
57 | }
58 |
59 | public static Method getDeclaredMethod(Class> cls, String methodName, Class>... paramTypes) {
60 | Method method;
61 | try {
62 | method = cls.getDeclaredMethod(methodName, paramTypes);
63 | } catch (NoSuchMethodException e) {
64 | throw new RuntimeException(e);
65 | }
66 | method.setAccessible(true);
67 | return method;
68 | }
69 |
70 | public static Method getMethod(Class> cls, String methodName, Class>... paramTypes) {
71 | Method method;
72 | try {
73 | method = cls.getMethod(methodName, paramTypes);
74 | } catch (NoSuchMethodException e) {
75 | throw new RuntimeException(e);
76 | }
77 | method.setAccessible(true);
78 | return method;
79 | }
80 |
81 | public static Object invoke(Method method, Object thisObject, Object... params) {
82 | try {
83 | return method.invoke(thisObject, params);
84 | } catch (InvocationTargetException e) {
85 | Throwable cause = e.getCause();
86 | if (cause instanceof RuntimeException) {
87 | throw (RuntimeException)cause;
88 | } else {
89 | throw new RuntimeException(e);
90 | }
91 | } catch (IllegalAccessException e) {
92 | throw new RuntimeException(e);
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/service/accessibility/BaseAccessibilityService.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.service.accessibility;
2 |
3 | import android.accessibilityservice.AccessibilityService;
4 | import android.content.ClipData;
5 | import android.content.ClipboardManager;
6 | import android.content.Context;
7 | import android.os.Build;
8 | import android.os.Bundle;
9 | import android.view.accessibility.AccessibilityEvent;
10 | import android.view.accessibility.AccessibilityNodeInfo;
11 |
12 | import java.util.List;
13 |
14 | public abstract class BaseAccessibilityService extends AccessibilityService {
15 |
16 | /**
17 | * 模拟点击事件
18 | *
19 | * @param nodeInfo nodeInfo
20 | */
21 | public void performViewClick(AccessibilityNodeInfo nodeInfo) {
22 | while (nodeInfo != null) {
23 | if (nodeInfo.isClickable()) {
24 | nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
25 | break;
26 | }
27 | nodeInfo = nodeInfo.getParent();
28 | }
29 | }
30 |
31 | /**
32 | * 查找对应文本的View
33 | *
34 | * @param text text
35 | * @param clickable clickable
36 | */
37 | public AccessibilityNodeInfo findViewByText(String text, boolean clickable) {
38 | AccessibilityNodeInfo rootNodeInfo = getRootInActiveWindow();
39 | if (rootNodeInfo == null) {
40 | return null;
41 | }
42 | List nodeInfoList = rootNodeInfo.findAccessibilityNodeInfosByText(text);
43 | if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
44 | for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
45 | if (nodeInfo != null && (nodeInfo.isClickable() == clickable)) {
46 | return nodeInfo;
47 | }
48 | }
49 | }
50 | return null;
51 | }
52 |
53 | /**
54 | * 模拟输入
55 | *
56 | * @param nodeInfo nodeInfo
57 | * @param text text
58 | */
59 | public void inputText(AccessibilityNodeInfo nodeInfo, CharSequence text) {
60 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
61 | Bundle args = new Bundle();
62 | args.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
63 | nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, args);
64 | } else {
65 | ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
66 | if (cm == null)
67 | return;
68 | ClipData clipData = ClipData.newPlainText("label", text);
69 | cm.setPrimaryClip(clipData);
70 | nodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
71 | nodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);
72 | }
73 | }
74 |
75 | /**
76 | * 模拟粘贴
77 | * @param nodeInfo nodeInfo
78 | */
79 | public void pasteText(AccessibilityNodeInfo nodeInfo) {
80 | nodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
81 | nodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);
82 | }
83 |
84 | @Override
85 | public void onAccessibilityEvent(AccessibilityEvent event) {
86 |
87 | }
88 |
89 | @Override
90 | public void onInterrupt() {
91 |
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_rule_edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
21 |
22 |
23 |
24 |
30 |
31 |
39 |
40 |
41 |
42 |
49 |
50 |
58 |
59 |
60 |
61 |
72 |
73 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/rule/TemplateRuleManager.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app.rule;
2 |
3 | import android.content.Context;
4 |
5 | import com.github.tianma8023.smscode.entity.SmsCodeRule;
6 | import com.github.tianma8023.smscode.utils.StorageUtils;
7 | import com.google.gson.Gson;
8 | import com.google.gson.GsonBuilder;
9 |
10 | import java.io.File;
11 | import java.io.FileInputStream;
12 | import java.io.FileNotFoundException;
13 | import java.io.FileOutputStream;
14 | import java.io.IOException;
15 | import java.io.InputStreamReader;
16 | import java.io.OutputStreamWriter;
17 | import java.nio.charset.StandardCharsets;
18 |
19 | public class TemplateRuleManager {
20 |
21 | private static final String RULE_TEMPLATE_DIRECTORY = "template";
22 |
23 | private static final String RULE_TEMPLATE_FILENAME = "rule-template.sce";
24 |
25 |
26 | /**
27 | * Get SMS code rule template dir
28 | */
29 | public static File getRuleTemplateDir(Context context) {
30 | if (StorageUtils.isSDCardMounted()) {
31 | return context.getExternalFilesDir(RULE_TEMPLATE_DIRECTORY);
32 | } else {
33 | return new File(context.getFilesDir(), RULE_TEMPLATE_DIRECTORY);
34 | }
35 | }
36 |
37 | /**
38 | * Get SMS code rule template file
39 | */
40 | public static File getRuleTemplateFile(Context context) {
41 | return new File(getRuleTemplateDir(context), RULE_TEMPLATE_FILENAME);
42 | }
43 |
44 | public static boolean saveTemplate(Context context, SmsCodeRule template) {
45 | File templateFile = getRuleTemplateFile(context);
46 | OutputStreamWriter osw = null;
47 | try {
48 | osw = new OutputStreamWriter(
49 | new FileOutputStream(templateFile), StandardCharsets.UTF_8);
50 | Gson gson = new GsonBuilder()
51 | .excludeFieldsWithoutExposeAnnotation()
52 | .create();
53 | gson.toJson(template, osw);
54 | return true;
55 | } catch (FileNotFoundException e) {
56 | e.printStackTrace();
57 | return false;
58 | } finally {
59 | if (osw != null) {
60 | try {
61 | osw.close();
62 | } catch (IOException e) {
63 | e.printStackTrace();
64 | }
65 | }
66 | }
67 | }
68 |
69 | public static SmsCodeRule loadTemplate(Context context) {
70 | File templateFile = getRuleTemplateFile(context);
71 | InputStreamReader isr = null;
72 | SmsCodeRule smsCodeRule = new SmsCodeRule();
73 | try {
74 | isr = new InputStreamReader(
75 | new FileInputStream(templateFile), StandardCharsets.UTF_8);
76 |
77 | Gson gson = new GsonBuilder()
78 | .excludeFieldsWithoutExposeAnnotation()
79 | .create();
80 | smsCodeRule = gson.fromJson(isr, SmsCodeRule.class);
81 | } catch (FileNotFoundException e) {
82 | e.printStackTrace();
83 | } finally {
84 | if (isr != null) {
85 | try {
86 | isr.close();
87 | } catch (IOException e) {
88 | e.printStackTrace();
89 | }
90 | }
91 | }
92 | return smsCodeRule;
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/preference/ResetEditPreferenceDialogFragCompat.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.preference;
2 |
3 | import android.content.DialogInterface;
4 | import android.os.Bundle;
5 | import android.view.View;
6 | import android.widget.EditText;
7 |
8 | import androidx.annotation.NonNull;
9 | import androidx.preference.PreferenceDialogFragmentCompat;
10 |
11 | public class ResetEditPreferenceDialogFragCompat extends PreferenceDialogFragmentCompat {
12 |
13 | private static final String SAVE_STATE_TEXT = "ResetEditPreferenceDialogFragCompat.text";
14 | private EditText mEditText;
15 | private CharSequence mText;
16 | private int mWhichClicked;
17 |
18 | public ResetEditPreferenceDialogFragCompat() {
19 | mWhichClicked = DialogInterface.BUTTON_NEUTRAL;
20 | }
21 |
22 | public static ResetEditPreferenceDialogFragCompat newInstance(String key) {
23 | ResetEditPreferenceDialogFragCompat fragment = new ResetEditPreferenceDialogFragCompat();
24 | Bundle b = new Bundle(1);
25 | b.putString("key", key);
26 | fragment.setArguments(b);
27 | return fragment;
28 | }
29 |
30 | public void onCreate(Bundle savedInstanceState) {
31 | super.onCreate(savedInstanceState);
32 | if (savedInstanceState == null) {
33 | this.mText = this.getResetEditPreference().getText();
34 | } else {
35 | this.mText = savedInstanceState.getCharSequence(SAVE_STATE_TEXT);
36 | }
37 |
38 | }
39 |
40 | public void onSaveInstanceState(@NonNull Bundle outState) {
41 | super.onSaveInstanceState(outState);
42 | outState.putCharSequence(SAVE_STATE_TEXT, this.mText);
43 | }
44 |
45 | protected void onBindDialogView(View view) {
46 | super.onBindDialogView(view);
47 | this.mEditText = view.findViewById(android.R.id.edit);
48 | this.mEditText.requestFocus();
49 | if (this.mEditText == null) {
50 | throw new IllegalStateException("Dialog view must contain an EditText with id @android:id/edit");
51 | } else {
52 | this.mEditText.setText(this.mText);
53 | this.mEditText.setSelection(this.mEditText.getText().length());
54 | }
55 | }
56 |
57 | protected boolean needInputMethod() {
58 | return true;
59 | }
60 |
61 | @Override
62 | public void onClick(DialogInterface dialog, int which) {
63 | super.onClick(dialog, which);
64 | mWhichClicked = which;
65 | }
66 |
67 | @Override
68 | public void onDismiss(DialogInterface dialog) {
69 | super.onDismiss(dialog);
70 | onDialogClosed();
71 | }
72 |
73 | @Override
74 | public void onDialogClosed(boolean positiveResult) {
75 | // do nothing
76 | }
77 |
78 | private ResetEditPreference getResetEditPreference() {
79 | return (ResetEditPreference) getPreference();
80 | }
81 |
82 | protected void onDialogClosed() {
83 | if (mWhichClicked == DialogInterface.BUTTON_NEUTRAL) {
84 | return;
85 | }
86 | ResetEditPreference resetEditPreference = getResetEditPreference();
87 | String value;
88 | if (mWhichClicked == DialogInterface.BUTTON_POSITIVE) {
89 | value = mEditText.getText().toString();
90 | } else {
91 | value = resetEditPreference.getDefaultValue();
92 | }
93 |
94 | if (resetEditPreference.callChangeListener(value)) {
95 | resetEditPreference.setText(value);
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/constant/PrefConst.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.constant;
2 |
3 |
4 | import com.github.tianma8023.smscode.BuildConfig;
5 |
6 | /**
7 | * Preference相关的常量
8 | */
9 | public interface PrefConst {
10 |
11 | String REMOTE_PREF_NAME = BuildConfig.APPLICATION_ID + "_preferences";
12 |
13 | String KEY_GENERAL = "pref_general";
14 |
15 | String ENABLE = "pref_enable";
16 | boolean ENABLE_DEFAULT = false;
17 |
18 | String LISTEN_MODE = "pref_listen_mode";
19 | String LISTEN_MODE_STANDARD = "listen_mode_standard";
20 | String LISTEN_MODE_COMPATIBLE = "listen_mode_compatible";
21 |
22 | String SHOW_TOAST = "pref_show_toast";
23 | boolean SHOW_TOAST_DEFAULT = true;
24 | String COPY_TO_CLIPBOARD = "pref_copy_to_clipboard";
25 | boolean COPY_TO_CLIPBOARD_DEFAULT = false;
26 |
27 | String CHOOSE_THEME = "pref_choose_theme";
28 | String CURRENT_THEME_INDEX = "pref_current_theme_index";
29 | int CURRENT_THEME_INDEX_DEFAULT = 0;
30 |
31 | String ENTRY_AUTO_INPUT_CODE = "pref_entry_auto_input_code";
32 |
33 | String EXPERIMENTAL = "pref_experimental";
34 | String MARK_AS_READ = "pref_mark_as_read";
35 | boolean MARK_AS_READ_DEFAULT = false;
36 | String DELETE_SMS = "pref_delete_sms";
37 | boolean DELETE_SMS_DEFAULT = false;
38 | String BLOCK_NOTIFICATION = "pref_block_notification";
39 | boolean BLOCK_NOTIFICATION_DEFAULT = false;
40 |
41 | String SHOW_CODE_NOTIFICATION = "pref_show_code_notification";
42 | boolean SHOW_CODE_NOTIFICATION_DEFAULT = true;
43 | String AUTO_CANCEL_CODE_NOTIFICATION = "pref_auto_cancel_code_notification";
44 | boolean AUTO_CANCEL_CODE_NOTIFICATION_DEFAULT = false;
45 | String NOTIFICATION_RETENTION_TIME = "pref_notification_retention_time";
46 | String NOTIFICATION_RETENTION_TIME_DEFAULT = "5";
47 |
48 |
49 | String SMSCODE_KEYWORDS = "pref_smscode_keywords";
50 | String SMSCODE_KEYWORDS_DEFAULT = SmsCodeConst.VERIFICATION_KEYWORDS_REGEX;
51 | String SMSCODE_TEST = "pref_smscode_test";
52 | String CODE_RULES = "pref_code_rules";
53 |
54 | String OTHERS = "pref_others";
55 | String EXCLUDE_FROM_RECENTS = "pref_exclude_from_recents";
56 |
57 | String ABOUT = "pref_about";
58 | String VERSION = "pref_version";
59 | String SOURCE_CODE = "pref_source_code";
60 | String RATING = "pref_rating";
61 | String GET_ALIPAY_PACKET = "pref_get_alipay_packet";
62 | String DONATE_BY_ALIPAY = "pref_donate_by_alipay";
63 |
64 | String ENABLE_AUTO_INPUT_CODE = "pref_enable_auto_input_code";
65 | boolean ENABLE_AUTO_INPUT_CODE_DEFAULT = false;
66 | String AUTO_INPUT_MODE = "pref_auto_input_mode";
67 | String AUTO_INPUT_MODE_DEFAULT = "";
68 | String AUTO_INPUT_MODE_ROOT = "auto_input_mode_root";
69 | String AUTO_INPUT_MODE_ACCESSIBILITY = "auto_input_mode_accessibility";
70 |
71 | String FOCUS_MODE = "pref_focus_mode";
72 | String FOCUS_MODE_AUTO = "focus_mode_auto";
73 | String FOCUS_MODE_MANUAL = "focus_mode_manual";
74 |
75 | String MANUAL_FOCUS_IF_FAILED = "pref_manual_focus_if_failed";
76 | boolean MANUAL_FOCUS_IF_FAILED_DEFAULT = false;
77 |
78 | String CLEAR_CLIPBOARD = "pref_clear_clipboard";
79 | boolean CLEAR_CLIPBOARD_DEFAULT = false;
80 |
81 | String VERBOSE_LOG_MODE = "pref_verbose_log_mode";
82 | boolean VERBOSE_LOG_MODE_DEFAULT = false;
83 |
84 | String ENABLE_CODE_RECORDS = "pref_enable_code_records";
85 | boolean ENABLE_CODE_RECORDS_DEFAULT = true;
86 | int MAX_SMS_RECORDS_COUNT_DEFAULT = 10;
87 | String ENTRY_CODE_RECORDS = "pref_entry_code_records";
88 | }
89 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/AccessibilityUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils;
2 |
3 | import android.accessibilityservice.AccessibilityService;
4 | import android.accessibilityservice.AccessibilityServiceInfo;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.provider.Settings;
8 | import android.view.accessibility.AccessibilityManager;
9 |
10 | import com.github.tianma8023.smscode.BuildConfig;
11 |
12 | import java.util.List;
13 |
14 | /**
15 | * AccessibilityService 相关 Utils
16 | */
17 | public class AccessibilityUtils {
18 |
19 | private AccessibilityUtils() {
20 | }
21 |
22 | private static AccessibilityManager getAccessibilityManager(Context context) {
23 | return (AccessibilityManager) context.getApplicationContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
24 | }
25 |
26 | /**
27 | * 检查当前辅助服务是否已启用
28 | *
29 | * @param context context
30 | * @param serviceId serviceId
31 | * @return 是否已启用
32 | */
33 | public static boolean checkAccessibilityEnabled(Context context, String serviceId) {
34 | AccessibilityManager accessibilityManager = getAccessibilityManager(context);
35 | List accessibilityServiceInfoList =
36 | accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
37 | for (AccessibilityServiceInfo info : accessibilityServiceInfoList) {
38 | if (info.getId().equals(serviceId)) {
39 | return true;
40 | }
41 | }
42 | return false;
43 | }
44 |
45 | /**
46 | * 前往开启无障碍服务界面
47 | *
48 | * @param context context
49 | */
50 | public static void gotoAccessibility(Context context) {
51 | Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
52 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
53 | context.startActivity(intent);
54 | }
55 |
56 | /**
57 | * 获取无障碍服务的ID
58 | *
59 | * @param serviceClz accessibility service class
60 | * @return accessibility service id
61 | */
62 | public static String getServiceId(Class extends AccessibilityService> serviceClz) {
63 | // eg.
64 | // service Class: com.github.tianma8023.smscode.service.accessibility.SmsCodeAutoInputService
65 | // package name: com.github.tianma8023.smscode
66 | // accessibility service id : com.github.tianma8023.smscode/.service.accessibility.SmsCodeAutoInputService
67 | String packageName = BuildConfig.APPLICATION_ID;
68 | String serviceClzName = serviceClz.getName();
69 | int index = serviceClzName.indexOf(packageName);
70 | if (index != -1) {
71 | return packageName + "/" + serviceClzName.substring(index + packageName.length());
72 | }
73 | return serviceClzName;
74 | }
75 |
76 | /**
77 | * 获取无障碍服务的名称
78 | *
79 | * @param serviceClz accessibility service class
80 | * @return accessibility service name
81 | */
82 | public static String getServiceName(Class extends AccessibilityService> serviceClz) {
83 | // eg.
84 | // serviceClassName = com.github.tianma8023.smscode.service.accessibility.SmsCodeAutoInputService
85 | // packageName = com.github.tianma8023.smscode
86 | // accessibility service id : ${packageName}/${serviceClassName}
87 | String packageName = BuildConfig.APPLICATION_ID;
88 | String serviceClzName = serviceClz.getName();
89 | return packageName + '/' + serviceClzName;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/app/SmsCodeApp.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.app;
2 |
3 | import android.app.Application;
4 | import android.app.NotificationChannel;
5 | import android.app.NotificationManager;
6 | import android.os.Build;
7 |
8 | import com.github.tianma8023.smscode.BuildConfig;
9 | import com.github.tianma8023.smscode.R;
10 | import com.github.tianma8023.smscode.constant.NotificationConst;
11 | import com.github.tianma8023.smscode.db.DBManager;
12 | import com.github.tianma8023.smscode.migrate.TransitionTask;
13 | import com.github.tianma8023.smscode.utils.CrashHandler;
14 | import com.github.tianma8023.smscode.utils.XLog;
15 | import com.tencent.bugly.crashreport.CrashReport;
16 |
17 | import java.util.concurrent.Executor;
18 | import java.util.concurrent.Executors;
19 |
20 | import androidx.annotation.RequiresApi;
21 |
22 | public class SmsCodeApp extends Application {
23 |
24 | @Override
25 | public void onCreate() {
26 | super.onCreate();
27 |
28 | initXLog();
29 |
30 | initCrashHandler();
31 |
32 | // initUmengAnalyze();
33 |
34 | initBugly();
35 |
36 | initNotificationChannel();
37 |
38 | initDatabase();
39 |
40 | // There is no need to perform data-transition now.
41 | // performTransitionTask();
42 | }
43 |
44 | private void initNotificationChannel() {
45 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
46 | // foreground service notification channel
47 | String channelId = NotificationConst.CHANNEL_ID_FOREGROUND_SERVICE;
48 | String channelName = getString(R.string.channel_name_foreground_service);
49 | createNotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_MIN);
50 |
51 | // SMS code notification channel
52 | channelId = NotificationConst.CHANNEL_ID_SMSCODE_NOTIFICATION;
53 | channelName = getString(R.string.channel_name_smscode_notification);
54 | createNotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH);
55 | }
56 | }
57 |
58 | @RequiresApi(api = Build.VERSION_CODES.O)
59 | private void createNotificationChannel(String channelId, String channelName, int importance) {
60 | NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
61 | NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
62 | if (manager != null) {
63 | manager.createNotificationChannel(channel);
64 | }
65 | }
66 |
67 | // umeng analyze initialization
68 | // private void initUmengAnalyze() {
69 | // UMConfigure.init(this, UMConfigure.DEVICE_TYPE_PHONE, "");
70 | // UMConfigure.setLogEnabled(BuildConfig.DEBUG);
71 | //
72 | // MobclickAgent.setScenarioType(this, MobclickAgent.EScenarioType.E_DUM_NORMAL);
73 | // MobclickAgent.setCatchUncaughtExceptions(false);
74 | // }
75 |
76 | // tencent bugly initialization
77 | private void initBugly() {
78 | CrashReport.initCrashReport(getApplicationContext(), "333c9e49e5", BuildConfig.DEBUG);
79 | }
80 |
81 | private void initXLog() {
82 | XLog.init(this);
83 | }
84 |
85 | // crash handler
86 | private void initCrashHandler() {
87 | CrashHandler.init(this, null);
88 | }
89 |
90 | private void initDatabase() {
91 | DBManager.get(this);
92 | }
93 |
94 | // data transition task
95 | private void performTransitionTask() {
96 | Executor singlePool = Executors.newSingleThreadExecutor();
97 | singlePool.execute(new TransitionTask(this));
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
17 |
18 |
23 |
24 |
29 |
30 |
35 |
36 |
41 |
42 |
47 |
48 |
53 |
54 |
59 |
60 |
70 |
71 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/LOG-EN.md:
--------------------------------------------------------------------------------
1 | # Update Logs
2 | - 20.01.13 v1.8.2
3 | 1. Fix: Rules import/export issue
4 | 2. Change: Show "Copy to Clipboard" option on Android 10
5 | - 19.08.06 v1.8.1
6 | 1. Fix: fix the issue of always showing parsing notification when non-code SMS arrives
7 | - 19.08.06 v1.8.0
8 | 1. Adapt to Android Q, upgrade targetSdk to Android Q
9 | 2. New option: auto cancel SMS code notification within certain time.
10 | 3. Change: migrate to androidx.
11 | 4. Change: beautify some UI.
12 | 5. Optimize: optimize the logic of parsing SMS code.
13 | 6. Fix: fix some logic and display errors.
14 | - 19.03.27 v1.7.3
15 | 1. Optimization: optimize the algorithm of parsing SMS code.
16 | 2. Fix: crash when sharing code rules file.
17 | - 19.03.18 v1.7.2
18 | 1. New: add an option for showing SMS code notification.
19 | 2. Optimization: optimize the algorithm of parsing SMS code.
20 | - 19.03.02 v1.7.1
21 | 1. New:add entry for rating on Cool Market.
22 | 2. Optimization: optimize some functions.
23 | - 19.02.04 v1.7.0 Happy Chinese New Year
24 | 1. Optimization: optimize the algorithm of parsing SMS code.
25 | - 19.01.21 v1.6.5
26 | 1. New: entry for ignoring battery optimization
27 | 2. Remove UMeng analyze
28 | 3. Optimize: adapt navigation bar color
29 | 4. Other bugs fix
30 | - 19.01.05 v1.6.4
31 | 1. Beautify parts of UI
32 | 2. Backup code rule files under /sdcard/Documents/
33 | - 19.01.04 v1.6.3
34 | 1. Adapt dark theme.
35 | 2. Add entry: get alipay red packet
36 | 3. Optimize the Auto-input mode selection and donation process.
37 | 4. Correct the English Copywriting.
38 | - 18.12.26 v1.6.2 the delayed Merry X'mas
39 | 1. Optimization: SMS message details can be shown in code records.
40 | 2. Change: don't exclude the App from recent-apps as default.
41 | 3. Other bugs fix.
42 | - 18.11.29 v1.6.1
43 | 1. Bug fix:code record company parse issue.
44 | - 18.11.21 v1.6.0
45 | 1. New experimental options: block code message notification
46 | 2. New theme color: teal.
47 | - 18.11.17 v1.5.0
48 | 1. Add configurable options: retain SMS code records.
49 | 2. Add App shortcuts.
50 | - 18.10.30 v1.4.2
51 | 1. Bug fix: crash when auto input.
52 | - 18.10.27 v1.4.1
53 | 1. Use v7 preference support library to fix the UI issues.
54 | - 18.10.26 v1.4.0
55 | 1. Upgrade target to Pie, Upgrade gradle build tools, no looger support kitkat(4.4.x).
56 | 2. Add icons for every preference.
57 | 3. Bug fix: cannot delete sms properly.
58 | 4. Remove 'default blue' and use 'pants blue' for the default theme color.
59 | - 18.10.23 v1.3.2
60 | 1. Add configurable options: exclude app from recent apps.
61 | 2. Add configurable options: delete verification sms if it's extracted succeed.
62 | 3. Add configurable options: copy sms code to clipboard.
63 | 4. Add configurable options: start manual focus mode if auto focus failed.
64 | 5. Add more user-friendly tips for settings.
65 | 6. Bug fixes.
66 | - 18.10.03 v1.3.1
67 | 1. Optimize the SMS code rule regex auto-generation.
68 | 2. Company and keywords are case insensitive for code rules.
69 | 3. bug fixes.
70 | - 18.09.30 v1.3.0
71 | 1. New feature: add the support of custom SMS code rules.
72 | 2. Move mark as read to experimental group.
73 | - 18.09.19 v1.2.0
74 | 1. New feature: mark sms as read.
75 | - 18.09.16 v1.1.1
76 | 1. Let "manual focus" to be the default focus mode.
77 | 2. Enhance the ability of auto-input in webview since Android Oreo.
78 | 3. Add the "动态口令" to SMS code keywords.
79 | - 18.09.13 v1.1.0
80 | 1. Merge "auto-input root mode" and "auto-input accessibility mode" into "auto-input mode".
81 | 2. New feature: clear clipboard if auto input succeed (optional).
82 | 3. Fix some bugs and possible problems.
83 | - 18.09.11 v1.0.1
84 | 1. Bug fix
85 | 2. New feature: show version info.
86 | 3. Reorder "debug" category.
87 | - 18.09.07 v1.0.0
88 | 1. The first version transplanted by XposedSmsCode.
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
23 | # obfuscate method & variables
24 | -obfuscationdictionary proguard-dictionary.txt
25 |
26 | # obfuscate class name
27 | -classobfuscationdictionary proguard-dictionary.txt
28 |
29 | # obfuscate package name
30 | -packageobfuscationdictionary proguard-dictionary.txt
31 |
32 | # repackage
33 | -repackageclasses android.support.v7
34 |
35 | # ===============================
36 | # AndPermission start
37 | -dontwarn com.yanzhenjie.permission.**
38 | # AndPermission end
39 | # ===============================
40 |
41 |
42 | # ===============================
43 | # logback-android start
44 | -dontwarn javax.mail.**
45 | -keep class ch.qos.** { *; }
46 | -keep class org.slf4j.** { *; }
47 | -keepattributes *Annotation*
48 | # logback-android end
49 | # ===============================
50 |
51 | # ==========================
52 | # Umeng analyze proguard start
53 |
54 | -keepclassmembers class * {
55 | public (org.json.JSONObject);
56 | }
57 |
58 | -keepclassmembers enum * {
59 | public static **[] values();
60 | public static ** valueOf(java.lang.String);
61 | }
62 |
63 | -keep class com.umeng.** {*;}
64 |
65 | # Umeng analyze proguard end
66 | # ==========================
67 |
68 | # ==========================
69 | # bugly proguard start
70 |
71 | -dontwarn com.tencent.bugly.**
72 | -keep public class com.tencent.bugly.** {
73 | *;
74 | }
75 |
76 | # bugly proguard end
77 | # ==========================
78 |
79 | # ==========================
80 | # event bus proguard start
81 |
82 | -keepattributes *Annotation*
83 | -keepclassmembers class * {
84 | @org.greenrobot.eventbus.Subscribe ;
85 | }
86 | -keep enum org.greenrobot.eventbus.ThreadMode { *; }
87 |
88 | # Only required if you use AsyncExecutor
89 | -keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
90 | (java.lang.Throwable);
91 | }
92 |
93 | # event bus proguard end
94 | # ==========================
95 |
96 | # ==========================
97 | # greenDAO 3 proguard start
98 | ### greenDAO 3
99 | ### GreenDaoUpgradeHelper
100 | -keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
101 | public static java.lang.String TABLENAME;
102 | public static void dropTable(org.greenrobot.greendao.database.Database, boolean);
103 | public static void createTable(org.greenrobot.greendao.database.Database, boolean);
104 | }
105 | -keep class **$Properties {
106 | *;
107 | }
108 |
109 | # If you do not use SQLCipher:
110 | -dontwarn org.greenrobot.greendao.database.**
111 | # If you do not use RxJava:
112 | -dontwarn rx.**
113 |
114 | # greenDAO 3 proguard end
115 | # ==========================
116 |
117 | # ==========================
118 | # BRVAH proguard start
119 | -keep class com.chad.library.adapter.** {
120 | *;
121 | }
122 | -keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter
123 | -keep public class * extends com.chad.library.adapter.base.BaseViewHolder
124 | -keepclassmembers class **$** extends com.chad.library.adapter.base.BaseViewHolder {
125 | (...);
126 | }
127 | -keepattributes InnerClasses
128 | # BRVAH proguard end
129 | # ==========================
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/utils/SettingsUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.utils;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.NotificationManager;
5 | import android.content.ComponentName;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.net.Uri;
9 | import android.os.Build;
10 | import android.provider.Settings;
11 | import android.text.TextUtils;
12 |
13 | import com.github.tianma8023.smscode.BuildConfig;
14 | import com.github.tianma8023.smscode.service.NotificationMonitorService;
15 |
16 | import androidx.annotation.RequiresApi;
17 |
18 | /**
19 | * Utility of android.provider.Settings
20 | */
21 | public class SettingsUtils {
22 |
23 | private SettingsUtils() {
24 | }
25 |
26 | private static String getSecureString(Context context, String key) {
27 | return Settings.Secure.getString(context.getContentResolver(), key);
28 | }
29 |
30 | /**
31 | * Get system default SMS app package
32 | * @return default SMS app package
33 | */
34 | public static String getDefaultSmsAppPackage(Context context) {
35 | String key = "sms_default_application";
36 | return Settings.Secure.getString(context.getContentResolver(), key);
37 | }
38 |
39 | /**
40 | * Check notification listener enabled or not.
41 | * @param context context
42 | * @return true if notification listener enabled, otherwise return false
43 | */
44 | public static boolean checkNotificationListenerEnabled(Context context) {
45 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O_MR1) {
46 | return checkNotificationListenerAbove27(context);
47 | } else {
48 | return checkNotificationListenerBelow27(context);
49 | }
50 | }
51 |
52 | @RequiresApi(api = Build.VERSION_CODES.O_MR1)
53 | private static boolean checkNotificationListenerAbove27(Context context) {
54 | NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
55 | boolean enabled = false;
56 | if (manager != null) {
57 | ComponentName cn = new ComponentName(context, NotificationMonitorService.class);
58 | enabled = manager.isNotificationListenerAccessGranted(cn);
59 | }
60 | return enabled;
61 | }
62 |
63 | private static boolean checkNotificationListenerBelow27(Context context) {
64 | String notifyStr = getSecureString(context, "enabled_notification_listeners");
65 | boolean enabled = false;
66 | ComponentName monitorService = new ComponentName(context, NotificationMonitorService.class);
67 | if (!TextUtils.isEmpty(notifyStr)) {
68 | String[] enabledServices = notifyStr.split(":");
69 | for(String enabledService : enabledServices) {
70 | ComponentName curCN = ComponentName.unflattenFromString(enabledService);
71 | if (monitorService.equals(curCN)) {
72 | enabled = true;
73 | break;
74 | }
75 | }
76 | }
77 | return enabled;
78 | }
79 |
80 | public static void gotoNotificationListenerSettings(Context context) {
81 | Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
82 | context.startActivity(intent);
83 | }
84 |
85 | /**
86 | * Request ignore battery optimization
87 | */
88 | @RequiresApi(api = Build.VERSION_CODES.M)
89 | @SuppressLint("BatteryLife")
90 | public static void requestIgnoreBatteryOptimization(Context context) {
91 | Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
92 | intent.setData(Uri.parse("package:" + BuildConfig.APPLICATION_ID));
93 | context.startActivity(intent);
94 | }
95 |
96 | /**
97 | * Go to ignore battery optimization settings.
98 | */
99 | @RequiresApi(api = Build.VERSION_CODES.M)
100 | public static void gotoIgnoreBatteryOptimizationSettings(Context context) {
101 | Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
102 | context.startActivity(intent);
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/tianma8023/smscode/entity/SmsMsg.java:
--------------------------------------------------------------------------------
1 | package com.github.tianma8023.smscode.entity;
2 |
3 | import android.os.Parcel;
4 | import android.os.Parcelable;
5 |
6 | import org.greenrobot.greendao.annotation.Entity;
7 | import org.greenrobot.greendao.annotation.Generated;
8 | import org.greenrobot.greendao.annotation.Id;
9 |
10 | import java.util.Objects;
11 |
12 | @Entity
13 | public class SmsMsg implements Parcelable {
14 |
15 | @Id(autoincrement = true)
16 | private Long id;
17 |
18 | // Sender
19 | private String sender;
20 |
21 | // Message content
22 | private String body;
23 |
24 | // Receive date
25 | private long date;
26 |
27 | // Company
28 | private String company;
29 |
30 | // SMS Code
31 | private String smsCode;
32 |
33 | public SmsMsg() {
34 | }
35 |
36 | private SmsMsg(Parcel source) {
37 | if (source.readByte() == 0) {
38 | id = null;
39 | } else {
40 | id = source.readLong();
41 | }
42 | sender = source.readString();
43 | body = source.readString();
44 | date = source.readLong();
45 | company = source.readString();
46 | smsCode = source.readString();
47 | }
48 |
49 | @Generated(hash = 1308161448)
50 | public SmsMsg(Long id, String sender, String body, long date, String company,
51 | String smsCode) {
52 | this.id = id;
53 | this.sender = sender;
54 | this.body = body;
55 | this.date = date;
56 | this.company = company;
57 | this.smsCode = smsCode;
58 | }
59 |
60 | public void setSender(String sender) {
61 | this.sender = sender;
62 | }
63 |
64 | public void setBody(String body) {
65 | this.body = body;
66 | }
67 |
68 | public void setDate(long date) {
69 | this.date = date;
70 | }
71 |
72 | public void setCompany(String company) {
73 | this.company = company;
74 | }
75 |
76 | public void setSmsCode(String smsCode) {
77 | this.smsCode = smsCode;
78 | }
79 |
80 | public String getSender() {
81 | return sender;
82 | }
83 |
84 | public String getBody() {
85 | return body;
86 | }
87 |
88 | public long getDate() {
89 | return date;
90 | }
91 |
92 | public String getCompany() {
93 | return company;
94 | }
95 |
96 | public String getSmsCode() {
97 | return smsCode;
98 | }
99 |
100 | @Override
101 | public int describeContents() {
102 | return 0;
103 | }
104 |
105 | @Override
106 | public void writeToParcel(Parcel dest, int flags) {
107 | if (id == null) {
108 | dest.writeByte((byte) 0);
109 | } else {
110 | dest.writeByte((byte) 1);
111 | dest.writeLong(id);
112 | }
113 | dest.writeString(sender);
114 | dest.writeString(body);
115 | dest.writeLong(date);
116 | dest.writeString(company);
117 | dest.writeString(smsCode);
118 | }
119 |
120 | public Long getId() {
121 | return this.id;
122 | }
123 |
124 | public void setId(Long id) {
125 | this.id = id;
126 | }
127 |
128 | public static final Creator CREATOR = new Creator() {
129 |
130 | @Override
131 | public SmsMsg createFromParcel(Parcel source) {
132 | return new SmsMsg(source);
133 | }
134 |
135 | @Override
136 | public SmsMsg[] newArray(int size) {
137 | return new SmsMsg[size];
138 | }
139 | };
140 |
141 | @Override
142 | public String toString() {
143 | return "SmsMsg{" +
144 | "id=" + id +
145 | ", date=" + date +
146 | ", company='" + company + '\'' +
147 | ", smsCode='" + smsCode + '\'' +
148 | '}';
149 | }
150 |
151 | @Override
152 | public boolean equals(Object o) {
153 | if (this == o) return true;
154 | if (!(o instanceof SmsMsg)) return false;
155 | SmsMsg smsMsg = (SmsMsg) o;
156 | return Objects.equals(id, smsMsg.id);
157 | }
158 |
159 | @Override
160 | public int hashCode() {
161 | return Objects.hash(id, sender, body, date, company, smsCode);
162 | }
163 | }
164 |
--------------------------------------------------------------------------------