├── 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 | 3 | 4 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /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 | 4 | 5 | 10 | 11 | 16 | 17 | -------------------------------------------------------------------------------- /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 | 4 | 5 | 10 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /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 | 4 | 5 | 10 | 11 | 15 | 16 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_records.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_edit_rule.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 16 | 17 | 21 | -------------------------------------------------------------------------------- /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 |