├── assets ├── icon │ ├── icon.png │ └── foreground.png └── screenshot │ └── ui.jpg ├── android ├── app │ ├── key │ │ └── sms.keystore │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── drawable │ │ │ │ │ ├── background.png │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-v21 │ │ │ │ │ ├── background.png │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values │ │ │ │ │ ├── colors.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── drawable-hdpi │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── drawable-mdpi │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── drawable-xhdpi │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── drawable-xxhdpi │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── drawable-xxxhdpi │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ └── ic_launcher.xml │ │ │ │ ├── values-v31 │ │ │ │ │ └── styles.xml │ │ │ │ ├── values-night-v31 │ │ │ │ │ └── styles.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── dc16 │ │ │ │ │ └── sms │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── key.properties ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle └── build.gradle ├── l10n.yaml ├── distribute_options.yaml ├── README_zh_TW.md ├── README_zh.md ├── .gitignore ├── .metadata ├── LICENSE ├── test └── widget_test.dart ├── README.md ├── .github └── workflows │ ├── publish.yml │ ├── build.yml │ └── manual.yml ├── analysis_options.yaml ├── lib ├── l10n │ ├── app_zh_TW.arb │ ├── app_zh.arb │ ├── app_en.arb │ └── generated │ │ ├── app_localizations_en.dart │ │ ├── app_localizations_zh.dart │ │ └── app_localizations.dart └── main.dart ├── pubspec.yaml ├── flutter_native_splash.yaml └── pubspec.lock /assets/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/assets/icon/icon.png -------------------------------------------------------------------------------- /assets/screenshot/ui.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/assets/screenshot/ui.jpg -------------------------------------------------------------------------------- /android/app/key/sms.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/android/app/key/sms.keystore -------------------------------------------------------------------------------- /assets/icon/foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/assets/icon/foreground.png -------------------------------------------------------------------------------- /android/key.properties: -------------------------------------------------------------------------------- 1 | storePassword=dc16@sms 2 | keyPassword=dc16@sms 3 | keyAlias=sms 4 | storeFile=key\\sms.keystore 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/android/app/src/main/res/drawable/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/android/app/src/main/res/drawable-v21/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /l10n.yaml: -------------------------------------------------------------------------------- 1 | arb-dir: lib/l10n 2 | output-dir: lib/l10n/generated/ 3 | template-arb-file: app_en.arb 4 | output-localization-file: app_localizations.dart 5 | format: true 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #2BAE67 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidche1116/Sms/HEAD/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.12-all.zip 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | .cxx/ 9 | 10 | # Remember to never publicly share your keystore. 11 | # See https://flutter.dev/to/reference-keystore 12 | key.properties 13 | **/*.keystore 14 | **/*.jks 15 | -------------------------------------------------------------------------------- /distribute_options.yaml: -------------------------------------------------------------------------------- 1 | output: dist/ 2 | releases: 3 | - name: apk 4 | jobs: 5 | - name: apk 6 | package: 7 | platform: android 8 | target: apk 9 | build_args: 10 | target-platform: android-arm64 11 | obfuscate: 12 | split-debug-info: build/symbols/apk 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README_zh_TW.md: -------------------------------------------------------------------------------- 1 | ![LOGO](android/app/src/main/res/mipmap-xhdpi/ic_launcher.png) 2 | 3 | # 簡訊清理 4 | 5 | [简体中文](README_zh.md) | 繁體中文 | [English](README.md) 6 | 7 | - 簡訊清理是使用Flutter框架編寫的Android平台上讀取、批量刪除簡訊的清理工具。 8 | - 雖然UI框架Flutter支援跨平台,但僅實現了Android簡訊刪除功能。 9 | 10 | ## 功能描述 11 | 12 | - 獲取簡訊權限 13 | - 複製簡訊到剪貼簿 14 | - 設置/恢復預設簡訊應用 15 | - 關鍵字過濾簡訊信息 16 | - 同號碼簡訊搜索 17 | - 從搜索結果移除/直接刪除簡訊 18 | - 一鍵批量刪除查詢結果簡訊 19 | - 一鍵導出所有簡訊到csv文件 20 | 21 | ## 界面截圖 22 | ![UI](assets/screenshot/ui.jpg) 23 | 24 | ## 開發環境 25 | ### [Flutter](https://docs.flutter.cn/get-started/install) 26 | - flutter stable 3.29.3 27 | - dart 3.7.2 28 | - gradle 8.12 29 | - gradle-plugin 8.7.3 30 | - kotlin 1.8.22 31 | 32 | ### 編譯命令 33 | - 安裝 flutter_distributor 34 | - `dart pub global activate flutter_distributor` 35 | - 打包release 36 | - `flutter_distributor release --name apk` 37 | -------------------------------------------------------------------------------- /README_zh.md: -------------------------------------------------------------------------------- 1 | ![LOGO](android/app/src/main/res/mipmap-xhdpi/ic_launcher.png) 2 | 3 | # 短信清理 4 | 5 | 简体中文 | [繁體中文](README_zh_TW.md) | [English](README.md) 6 | 7 | - 短信清理是Flutter框架编写的在Android上读取、批量删除短信的短信清理工具。 8 | - 虽然UI框架Flutter支持跨平台,但仅实现了Android短信删除功能。 9 | 10 | ## 功能描述 11 | 12 | - 获取短信权限 13 | - 复制短信到剪切板 14 | - 设置/恢复默认短信应用 15 | - 关键字过滤短信信息 16 | - 同号码短信搜索 17 | - 从搜索结果移除/直接删除短信 18 | - 一键批量删除查询结果短信 19 | - 一键导出所有短信到csv文件 20 | 21 | ## 界面截图 22 | ![UI](assets/screenshot/ui.jpg) 23 | 24 | 25 | ## 开发环境 26 | ### [Flutter](https://docs.flutter.cn/get-started/install) 27 | - flutter stable 3.29.3 28 | - dart 3.7.2 29 | - gradle 8.12 30 | - gradle-plugin 8.7.3 31 | - kotlin 1.8.22 32 | 33 | ### 编译命令 34 | - 安装 flutter_distributor 35 | - `dart pub global activate flutter_distributor` 36 | - 打包release 37 | - `flutter_distributor release --name apk` 38 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | }() 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 21 | id "com.android.application" version "8.9.1" apply false 22 | id "org.jetbrains.kotlin.android" version "2.1.0" apply false 23 | } 24 | 25 | include ":app" 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .pub-cache/ 33 | .pub/ 34 | /build/ 35 | /coverage/ 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | /dist/ 48 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | subprojects { 8 | afterEvaluate { project -> 9 | if (project.hasProperty('android')) { 10 | project.android { 11 | if (namespace == null) { 12 | namespace project.group 13 | } 14 | } 15 | } 16 | } 17 | } 18 | rootProject.buildDir = "../build" 19 | subprojects { 20 | project.buildDir = "${rootProject.buildDir}/${project.name}" 21 | } 22 | subprojects { 23 | project.evaluationDependsOn(":app") 24 | } 25 | subprojects { 26 | buildscript { 27 | ext.kotlin_version = '2.1.0' 28 | dependencies { 29 | classpath 'com.android.tools.build:gradle:8.9.1' 30 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 31 | } 32 | } 33 | } 34 | tasks.register("clean", Delete) { 35 | delete rootProject.buildDir 36 | } 37 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "20f82749394e68bcfbbeee96bad384abaae09c13" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13 17 | base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13 18 | - platform: android 19 | create_revision: 20f82749394e68bcfbbeee96bad384abaae09c13 20 | base_revision: 20f82749394e68bcfbbeee96bad384abaae09c13 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 davidche 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:sms/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const SmsApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![LOGO](android/app/src/main/res/mipmap-xhdpi/ic_launcher.png) 2 | 3 | # SMS Cleaner 4 | 5 | [简体中文](README_zh.md) | [繁體中文](README_zh_TW.md) | English 6 | 7 | - SMS Cleaner is a tool for reading and batch deleting SMS messages on Android, built with Flutter framework. 8 | - Although the UI framework Flutter supports cross-platform development, only Android SMS deletion functionality has been implemented. 9 | 10 | ## Features 11 | 12 | - Get SMS permissions 13 | - Copy SMS to clipboard 14 | - Set/restore default SMS app 15 | - Filter SMS messages by keywords 16 | - Search SMS by phone number 17 | - Remove/directly delete SMS from search results 18 | - One-click batch deletion of queried SMS messages 19 | - One-click export of all SMS messages to CSV file 20 | 21 | ## Screenshot 22 | ![UI](assets/screenshot/ui.jpg) 23 | 24 | ## Development Environment 25 | ### [Flutter](https://docs.flutter.cn/get-started/install) 26 | - flutter stable 3.35.1 27 | - dart 3.9.0 28 | - gradle 8.12 29 | - gradle-plugin 8.9.1 30 | - kotlin 2.1.0 31 | 32 | ### Build Commands 33 | - Install flutter_distributor 34 | - `dart pub global activate flutter_distributor` 35 | - Build release 36 | - `flutter_distributor release --name apk` 37 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 18 | 21 | 22 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Build and publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - '[0-9]+.[0-9]+.[0-9]+*' 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@main 14 | 15 | - name: Set Up Java 16 | uses: actions/setup-java@v4 17 | with: 18 | distribution: "oracle" 19 | cache: 'gradle' 20 | java-version: "17" 21 | 22 | - name: Set Up Flutter 23 | uses: subosito/flutter-action@v2 24 | with: 25 | channel: "beta" 26 | cache: true 27 | 28 | - name: Install Dependencies 29 | run: flutter pub get 30 | shell: bash 31 | 32 | - name: Install flutter_distributor 33 | run: dart pub global activate flutter_distributor 34 | shell: bash 35 | 36 | - name: Build APK 37 | run: flutter_distributor release --name apk 38 | shell: bash 39 | 40 | - name: Release APK 41 | uses: softprops/action-gh-release@v2 42 | if: startsWith(github.ref, 'refs/tags/') 43 | with: 44 | files: | 45 | dist/** 46 | draft: true 47 | token: ${{ secrets.SMS_RELEASE }} 48 | 49 | - name: Archive APK 50 | uses: actions/upload-artifact@v4 51 | with: 52 | name: sms 53 | path: | 54 | dist/** 55 | overwrite: true 56 | compression-level: 9 57 | retention-days: 2 58 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at https://dart.dev/lints. 17 | # 18 | # Instead of disabling a lint rule for the entire project in the 19 | # section below, it can also be suppressed for a single line of code 20 | # or a specific dart file by using the `// ignore: name_of_lint` and 21 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 22 | # producing the lint. 23 | rules: 24 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 25 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 26 | 27 | # Additional information about this file can be found at 28 | # https://dart.dev/guides/language/analysis-options 29 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and archive 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags-ignore: 8 | - '[0-9]+.[0-9]+.[0-9]+*' 9 | pull_request: 10 | types: 11 | - opened 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@main 19 | 20 | - name: Set Up Java 21 | uses: actions/setup-java@v4 22 | with: 23 | distribution: "oracle" 24 | cache: 'gradle' 25 | java-version: "17" 26 | 27 | - name: Set Up Flutter 28 | uses: subosito/flutter-action@v2 29 | with: 30 | channel: 'stable' 31 | cache: true 32 | 33 | - name: Install Dependencies 34 | run: flutter pub get 35 | shell: bash 36 | 37 | - name: Install flutter_distributor 38 | run: dart pub global activate flutter_distributor 39 | shell: bash 40 | 41 | - name: Checking flutter 42 | run: flutter doctor --verbose 43 | shell: bash 44 | 45 | - name: Analyze project source 46 | run: dart analyze 47 | shell: bash 48 | 49 | - name: Build APK 50 | run: flutter_distributor release --name apk 51 | shell: bash 52 | 53 | - name: Archive APK 54 | uses: actions/upload-artifact@v4 55 | with: 56 | name: sms 57 | path: | 58 | dist/** 59 | overwrite: true 60 | compression-level: 9 61 | retention-days: 2 62 | -------------------------------------------------------------------------------- /.github/workflows/manual.yml: -------------------------------------------------------------------------------- 1 | name: Manual operation 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | flutter_channel: 7 | default: 'beta' 8 | required: true 9 | type: choice 10 | options: 11 | - beta 12 | - master 13 | - stable 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@main 21 | 22 | - name: Set Up Java 23 | uses: actions/setup-java@v4 24 | with: 25 | distribution: "oracle" 26 | cache: 'gradle' 27 | java-version: "17" 28 | 29 | - name: Set Up Flutter 30 | uses: subosito/flutter-action@v2 31 | with: 32 | channel: ${{ inputs.flutter_channel }} 33 | cache: true 34 | 35 | - name: Install Dependencies 36 | run: flutter pub get 37 | shell: bash 38 | 39 | - name: Install flutter_distributor 40 | run: dart pub global activate flutter_distributor 41 | shell: bash 42 | 43 | - name: Checking flutter 44 | run: flutter doctor --verbose 45 | shell: bash 46 | 47 | - name: Analyze project source 48 | run: dart analyze 49 | shell: bash 50 | 51 | - name: Build APK 52 | run: flutter_distributor release --name apk 53 | shell: bash 54 | 55 | - name: Archive APK 56 | uses: actions/upload-artifact@v4 57 | with: 58 | name: sms 59 | path: | 60 | dist/** 61 | overwrite: true 62 | compression-level: 9 63 | -------------------------------------------------------------------------------- /lib/l10n/app_zh_TW.arb: -------------------------------------------------------------------------------- 1 | { 2 | "@@locale": "zh_TW", 3 | "title": "簡訊清理", 4 | "toast_default": "需要在系統設定中將本應用程式設定為預設簡訊應用程式才能刪除簡訊", 5 | "toast_permission": "需要申請簡訊權限或設定為預設簡訊應用程式", 6 | "keyword": "關鍵字", 7 | "b_confirm": "確定", 8 | "t_confirm_delete": "確認刪除", 9 | "delete_num": "是否刪除這{num}則簡訊?", 10 | "@delete_num": { 11 | "description": "帶有單個參數的訊息", 12 | "placeholders": { 13 | "num": { 14 | "type": "String", 15 | "example": "1" 16 | } 17 | } 18 | }, 19 | "b_cancel": "取消", 20 | "operation_failed": "操作失敗", 21 | "operation_completed": "操作成功", 22 | "toast_no": "沒有簡訊可分享", 23 | "sms_list": "簡訊清單", 24 | "toast_share": "分享成功,請使用utf-8格式開啟", 25 | "tips": "提示", 26 | "delete_or_move": "是否要刪除或移出目前項目?", 27 | "b_remove": "移出清單", 28 | "b_delete": "直接刪除", 29 | "b_same_number": "相同號碼簡訊", 30 | "b_same_sim": "相同卡簡訊", 31 | "toast_clipboard": "已複製到剪貼簿", 32 | "b_copy": "複製", 33 | "sms": "簡訊", 34 | "num_sms": "{num}則簡訊", 35 | "@num_sms": { 36 | "description": "帶有單個參數的訊息", 37 | "placeholders": { 38 | "num": { 39 | "type": "String", 40 | "example": "1" 41 | } 42 | } 43 | }, 44 | "t_all_sms": "所有簡訊清單", 45 | "t_date_filter": "日期範圍過濾", 46 | "t_keyword_filter": "關鍵字篩選", 47 | "set_permission": "申請簡訊權限", 48 | "set_settings": "應用程式權限設定", 49 | "set_default": "設定為預設簡訊應用程式", 50 | "set_restore": "還原預設簡訊應用程式", 51 | "set_export": "匯出csv並分享", 52 | "t_wait": "請稍候", 53 | "t_no_sms": "沒有簡訊", 54 | "b_remove_filter": "移除篩選條件", 55 | "t_delete_all": "刪除目前清單中的所有簡訊", 56 | "sim": "卡", 57 | "t_list_too_long": "清單較長,可能需要較多時間" 58 | } 59 | -------------------------------------------------------------------------------- /lib/l10n/app_zh.arb: -------------------------------------------------------------------------------- 1 | { 2 | "@@locale": "zh", 3 | "title": "短信清理", 4 | "toast_default": "需要在系统设置中将本应用设置为默认短信应用才能删除短信", 5 | "toast_permission": "需要申请短信权限或设置为短信默认应用", 6 | "keyword": "关键字", 7 | "b_confirm": "确定", 8 | "t_confirm_delete": "确认删除", 9 | "delete_num": "是否删除这{num}条短信?", 10 | "@delete_num": { 11 | "description": "A message with a single parameter", 12 | "placeholders": { 13 | "num": { 14 | "type": "String", 15 | "example": "1" 16 | } 17 | } 18 | }, 19 | "b_cancel": "取消", 20 | "operation_failed": "操作失败", 21 | "operation_completed": "操作成功", 22 | "toast_no": "没有短信可分享", 23 | "sms_list": "短信列表", 24 | "toast_share": "分享成功,需用utf-8格式打开", 25 | "tips": "提示", 26 | "delete_or_move": "是否要删除或移出当前项?", 27 | "b_remove": "移出列表", 28 | "b_delete": "直接删除", 29 | "b_same_number": "同号短信", 30 | "b_same_sim": "同卡短信", 31 | "toast_clipboard": "已复制到剪切板", 32 | "b_copy": "复制", 33 | "sms": "短信", 34 | "num_sms": "{num}条短信", 35 | "@num_sms": { 36 | "description": "A message with a single parameter", 37 | "placeholders": { 38 | "num": { 39 | "type": "String", 40 | "example": "1" 41 | } 42 | } 43 | }, 44 | "t_all_sms": "所有短信列表", 45 | "t_date_filter": "日期范围过滤", 46 | "t_keyword_filter": "关键字过滤", 47 | "set_permission": "申请短信权限", 48 | "set_settings": "应用权限设置", 49 | "set_default": "设置默认短信应用", 50 | "set_restore": "恢复默认短信应用", 51 | "set_export": "导出csv并分享", 52 | "t_wait": "请稍等", 53 | "t_no_sms": "没有短信", 54 | "b_remove_filter": "移除过滤条件", 55 | "t_delete_all": "删除当前列表所有短信", 56 | "sim": "卡", 57 | "t_list_too_long": "列表太多了,可能需要久一点" 58 | } 59 | -------------------------------------------------------------------------------- /lib/l10n/app_en.arb: -------------------------------------------------------------------------------- 1 | { 2 | "@@locale": "en", 3 | "title": "SMS Cleanup", 4 | "toast_default": "To delete SMS messages, please set this app as the default SMS app in System Settings", 5 | "toast_permission": "SMS permission required or set as default SMS app", 6 | "keyword": "Keyword", 7 | "b_confirm": "Confirm", 8 | "t_confirm_delete": "Confirm Deletion", 9 | "delete_num": "Delete {num} message(s)?", 10 | "@delete_num": { 11 | "description": "A message with a single parameter", 12 | "placeholders": { 13 | "num": { 14 | "type": "String", 15 | "example": "1" 16 | } 17 | } 18 | }, 19 | "b_cancel": "Cancel", 20 | "operation_failed": "Operation Failed", 21 | "operation_completed": "Operation Completed", 22 | "toast_no": "No messages to share", 23 | "sms_list": "Message List", 24 | "toast_share": "Shared successfully. Please open with UTF-8 encoding", 25 | "tips": "Tips", 26 | "delete_or_move": "Delete or remove this item?", 27 | "b_remove": "Remove from List", 28 | "b_delete": "Delete", 29 | "b_same_number": "Messages from Same Number", 30 | "b_same_sim": "Messages from Same SIM", 31 | "toast_clipboard": "Copied to clipboard", 32 | "b_copy": "Copy", 33 | "sms": "SMS", 34 | "num_sms": "{num} message(s)", 35 | "@num_sms": { 36 | "description": "A message with a single parameter", 37 | "placeholders": { 38 | "num": { 39 | "type": "String", 40 | "example": "1" 41 | } 42 | } 43 | }, 44 | "t_all_sms": "All Messages", 45 | "t_date_filter": "Filter by date", 46 | "t_keyword_filter": "Filter by Keyword", 47 | "set_permission": "Request SMS Permission", 48 | "set_settings": "App Settings", 49 | "set_default": "Set as Default SMS App", 50 | "set_restore": "Restore Default SMS App", 51 | "set_export": "Export as CSV", 52 | "t_wait": "Please wait", 53 | "t_no_sms": "No messages", 54 | "b_remove_filter": "Clear Filter", 55 | "t_delete_all": "Delete All Messages in Current List", 56 | "sim": "SIM", 57 | "t_list_too_long": "Large list, may take longer to process" 58 | } 59 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 5 | id "dev.flutter.flutter-gradle-plugin" 6 | } 7 | 8 | def keystoreProperties = new Properties() 9 | def keystorePropertiesFile = rootProject.file('key.properties') 10 | if (keystorePropertiesFile.exists()) { 11 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 12 | } 13 | 14 | android { 15 | namespace = "com.dc16.sms" 16 | compileSdk = flutter.compileSdkVersion 17 | ndkVersion = flutter.ndkVersion 18 | 19 | compileOptions { 20 | sourceCompatibility = JavaVersion.VERSION_17 21 | targetCompatibility = JavaVersion.VERSION_17 22 | } 23 | 24 | kotlinOptions { 25 | jvmTarget = JavaVersion.VERSION_17 26 | } 27 | 28 | defaultConfig { 29 | // Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 30 | applicationId = "com.dc16.sms" 31 | // You can update the following values to match your application needs. 32 | // For more information, see: https://flutter.dev/to/review-gradle-config. 33 | minSdk = 26 34 | targetSdk = flutter.targetSdkVersion 35 | versionCode = flutter.versionCode 36 | versionName = flutter.versionName 37 | } 38 | 39 | signingConfigs { 40 | release { 41 | keyAlias keystoreProperties['keyAlias'] 42 | keyPassword keystoreProperties['keyPassword'] 43 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 44 | storePassword keystoreProperties['storePassword'] 45 | } 46 | } 47 | 48 | buildTypes { 49 | release { 50 | // Add your own signing config for the release build. 51 | // Signing with the debug keys for now, so `flutter run --release` works. 52 | signingConfig = signingConfigs.release 53 | minifyEnabled = true 54 | shrinkResources = true 55 | 56 | ndk { 57 | //noinspection ChromeOsAbiSupport 58 | abiFilters 'arm64-v8a' 59 | } 60 | } 61 | debug { 62 | signingConfig = signingConfigs.release 63 | } 64 | } 65 | } 66 | 67 | flutter { 68 | source = "../.." 69 | } 70 | -------------------------------------------------------------------------------- /lib/l10n/generated/app_localizations_en.dart: -------------------------------------------------------------------------------- 1 | // ignore: unused_import 2 | import 'package:intl/intl.dart' as intl; 3 | import 'app_localizations.dart'; 4 | 5 | // ignore_for_file: type=lint 6 | 7 | /// The translations for English (`en`). 8 | class AppLocalizationsEn extends AppLocalizations { 9 | AppLocalizationsEn([String locale = 'en']) : super(locale); 10 | 11 | @override 12 | String get title => 'SMS Cleanup'; 13 | 14 | @override 15 | String get toast_default => 16 | 'To delete SMS messages, please set this app as the default SMS app in System Settings'; 17 | 18 | @override 19 | String get toast_permission => 20 | 'SMS permission required or set as default SMS app'; 21 | 22 | @override 23 | String get keyword => 'Keyword'; 24 | 25 | @override 26 | String get b_confirm => 'Confirm'; 27 | 28 | @override 29 | String get t_confirm_delete => 'Confirm Deletion'; 30 | 31 | @override 32 | String delete_num(String num) { 33 | return 'Delete $num message(s)?'; 34 | } 35 | 36 | @override 37 | String get b_cancel => 'Cancel'; 38 | 39 | @override 40 | String get operation_failed => 'Operation Failed'; 41 | 42 | @override 43 | String get operation_completed => 'Operation Completed'; 44 | 45 | @override 46 | String get toast_no => 'No messages to share'; 47 | 48 | @override 49 | String get sms_list => 'Message List'; 50 | 51 | @override 52 | String get toast_share => 53 | 'Shared successfully. Please open with UTF-8 encoding'; 54 | 55 | @override 56 | String get tips => 'Tips'; 57 | 58 | @override 59 | String get delete_or_move => 'Delete or remove this item?'; 60 | 61 | @override 62 | String get b_remove => 'Remove from List'; 63 | 64 | @override 65 | String get b_delete => 'Delete'; 66 | 67 | @override 68 | String get b_same_number => 'Messages from Same Number'; 69 | 70 | @override 71 | String get b_same_sim => 'Messages from Same SIM'; 72 | 73 | @override 74 | String get toast_clipboard => 'Copied to clipboard'; 75 | 76 | @override 77 | String get b_copy => 'Copy'; 78 | 79 | @override 80 | String get sms => 'SMS'; 81 | 82 | @override 83 | String num_sms(String num) { 84 | return '$num message(s)'; 85 | } 86 | 87 | @override 88 | String get t_all_sms => 'All Messages'; 89 | 90 | @override 91 | String get t_date_filter => 'Filter by date'; 92 | 93 | @override 94 | String get t_keyword_filter => 'Filter by Keyword'; 95 | 96 | @override 97 | String get set_permission => 'Request SMS Permission'; 98 | 99 | @override 100 | String get set_settings => 'App Settings'; 101 | 102 | @override 103 | String get set_default => 'Set as Default SMS App'; 104 | 105 | @override 106 | String get set_restore => 'Restore Default SMS App'; 107 | 108 | @override 109 | String get set_export => 'Export as CSV'; 110 | 111 | @override 112 | String get t_wait => 'Please wait'; 113 | 114 | @override 115 | String get t_no_sms => 'No messages'; 116 | 117 | @override 118 | String get b_remove_filter => 'Clear Filter'; 119 | 120 | @override 121 | String get t_delete_all => 'Delete All Messages in Current List'; 122 | 123 | @override 124 | String get sim => 'SIM'; 125 | 126 | @override 127 | String get t_list_too_long => 'Large list, may take longer to process'; 128 | } 129 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/dc16/sms/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.dc16.sms 2 | 3 | import android.app.role.RoleManager 4 | import android.content.Intent 5 | import android.content.pm.PackageManager 6 | import android.os.Build 7 | import android.provider.Telephony 8 | import androidx.annotation.NonNull 9 | import io.flutter.embedding.android.FlutterFragmentActivity 10 | import io.flutter.embedding.engine.FlutterEngine 11 | import io.flutter.plugin.common.MethodChannel 12 | 13 | /** 14 | * MainActivity类负责处理SMS应用的主要功能 15 | * 包括获取、设置和重置默认短信应用 16 | */ 17 | class MainActivity : FlutterFragmentActivity() { 18 | private val CHANNEL = "com.dc16.sms/smsApp" 19 | 20 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { 21 | super.configureFlutterEngine(flutterEngine) 22 | MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> 23 | when (call.method) { 24 | "getDefaultSmsApp" -> result.success(getDefaultSmsApp()) 25 | "setDefaultSmsApp" -> result.success(setDefaultSmsApp()) 26 | "resetDefaultSmsApp" -> result.success(resetDefaultSmsApp()) 27 | else -> result.notImplemented() 28 | } 29 | } 30 | } 31 | 32 | /** 33 | * 获取当前默认短信应用 34 | * @return 返回当前默认短信应用的包名,如果没有则返回空字符串 35 | */ 36 | private fun getDefaultSmsApp(): String { 37 | val defaultSmsApp = Telephony.Sms.getDefaultSmsPackage(this) 38 | if (defaultSmsApp == null) { 39 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { 40 | val roleManager = getSystemService(RoleManager::class.java) 41 | val isRoleHeld = roleManager?.isRoleHeld(RoleManager.ROLE_SMS) ?: false 42 | if (isRoleHeld) { 43 | return packageName 44 | } 45 | return "" 46 | } 47 | } 48 | return defaultSmsApp ?: "" 49 | } 50 | 51 | /** 52 | * 设置当前应用为默认短信应用 53 | * @return 返回设置状态:"had"(已经是默认应用),"ok"(设置成功),"no"(需要用户确认) 54 | */ 55 | private fun setDefaultSmsApp(): String { 56 | val packageName = this.packageName 57 | val defaultName = getDefaultSmsApp() 58 | 59 | if (defaultName.isEmpty() || packageName != defaultName) { 60 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { 61 | val roleManager = getSystemService(RoleManager::class.java) 62 | val isRoleHeld = roleManager?.isRoleHeld(RoleManager.ROLE_SMS) ?: false 63 | if (isRoleHeld) { 64 | return "had" 65 | } 66 | val roleRequestIntent = roleManager?.createRequestRoleIntent(RoleManager.ROLE_SMS) 67 | roleRequestIntent?.let { 68 | startActivityForResult(it, 12) 69 | } 70 | } else { 71 | val intent = Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT) 72 | intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, packageName) 73 | startActivity(intent) 74 | } 75 | return "no" 76 | } 77 | return "ok" 78 | } 79 | 80 | /** 81 | * 重置默认短信应用为系统应用 82 | * @return 返回重置状态:"ok"(重置成功),"no"(重置失败) 83 | */ 84 | private fun resetDefaultSmsApp(): String { 85 | val packageManager = packageManager 86 | 87 | // 尝试启动Android默认短信应用 88 | packageManager.getLaunchIntentForPackage("com.android.mms")?.let { intent -> 89 | startActivity(intent) 90 | return "ok" 91 | } 92 | 93 | // 尝试启动Google短信应用 94 | packageManager.getLaunchIntentForPackage("com.google.android.apps.messaging")?.let { intent -> 95 | startActivity(intent) 96 | return "ok" 97 | } 98 | 99 | return "no" 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: sms 2 | description: "SMS cleaning tool application Flutter project." 3 | # The following line prevents the package from being accidentally published to 4 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 6 | homepage: https://github.com/davidche1116/Sms 7 | repository: https://github.com/davidche1116/Sms 8 | issue_tracker: https://github.com/davidche1116/Sms/issues 9 | 10 | # The following defines the version and build number for your application. 11 | # A version number is three numbers separated by dots, like 1.2.43 12 | # followed by an optional build number separated by a +. 13 | # Both the version and the builder number may be overridden in flutter 14 | # build by specifying --build-name and --build-number, respectively. 15 | # In Android, build-name is used as versionName while build-number used as versionCode. 16 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 17 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. 18 | # Read more about iOS versioning at 19 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 20 | # In Windows, build-name is used as the major, minor, and patch parts 21 | # of the product and file versions while build-number is used as the build suffix. 22 | version: 1.6.1+250725 23 | 24 | environment: 25 | sdk: ^3.9.0 26 | 27 | # Dependencies specify other packages that your package needs in order to work. 28 | # To automatically upgrade your package dependencies to the latest versions 29 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 30 | # dependencies can be manually updated by changing the version numbers below to 31 | # the latest version available on pub.dev. To see which dependencies have newer 32 | # versions available, run `flutter pub outdated`. 33 | dependencies: 34 | flutter: 35 | sdk: flutter 36 | 37 | flutter_localizations: 38 | sdk: flutter 39 | 40 | # 跨平台文件 41 | cross_file: ^0.3.4+2 42 | # 导出csv 43 | csv: ^6.0.0 44 | # 对话框loading 45 | flutter_smart_dialog: ^4.9.8+9 46 | # 国际化 47 | intl: any 48 | # 获取临时目录 49 | path_provider: ^2.1.5 50 | # 权限申请 51 | permission_handler: ^12.0.1 52 | # 分享 53 | share_plus: ^11.1.0 54 | # 短信读取、删除 55 | sms_advanced: ^1.1.0 56 | 57 | dev_dependencies: 58 | flutter_test: 59 | sdk: flutter 60 | 61 | # The "flutter_lints" package below contains a set of recommended lints to 62 | # encourage good coding practices. The lint set provided by the package is 63 | # activated in the `analysis_options.yaml` file located at the root of your 64 | # package. See that file for information about deactivating specific lint 65 | # rules and activating additional ones. 66 | flutter_lints: ^6.0.0 67 | 68 | flutter_launcher_icons: ^0.14.4 69 | 70 | flutter_native_splash: ^2.4.6 71 | 72 | # For information on the generic Dart part of this file, see the 73 | # following page: https://dart.dev/tools/pub/pubspec 74 | 75 | # The following section is specific to Flutter packages. 76 | flutter: 77 | # 添加代码生成(合成包)支持 78 | generate: true 79 | 80 | # The following line ensures that the Material Icons font is 81 | # included with your application, so that you can use the icons in 82 | # the material Icons class. 83 | uses-material-design: true 84 | 85 | # To add assets to your application, add an assets section, like this: 86 | # assets: 87 | # - images/a_dot_burr.jpeg 88 | # - images/a_dot_ham.jpeg 89 | 90 | # An image asset can refer to one or more resolution-specific "variants", see 91 | # https://flutter.dev/to/resolution-aware-images 92 | 93 | # For details regarding adding assets from package dependencies, see 94 | # https://flutter.dev/to/asset-from-package 95 | 96 | # To add custom fonts to your application, add a fonts section here, 97 | # in this "flutter" section. Each entry in this list should have a 98 | # "family" key with the font family name, and a "fonts" key with a 99 | # list giving the asset and other descriptors for the font. For 100 | # example: 101 | # fonts: 102 | # - family: Schyler 103 | # fonts: 104 | # - asset: fonts/Schyler-Regular.ttf 105 | # - asset: fonts/Schyler-Italic.ttf 106 | # style: italic 107 | # - family: Trajan Pro 108 | # fonts: 109 | # - asset: fonts/TrajanPro.ttf 110 | # - asset: fonts/TrajanPro_Bold.ttf 111 | # weight: 700 112 | # 113 | # For details regarding fonts from package dependencies, 114 | # see https://flutter.dev/to/font-from-package 115 | 116 | flutter_launcher_icons: 117 | android: "ic_launcher" 118 | ios: false 119 | image_path: "assets/icon/icon.png" 120 | min_sdk_android: 26 # android min sdk min:16, default 21 121 | adaptive_icon_background: "#2BAE67" 122 | adaptive_icon_foreground: "assets/icon/foreground.png" 123 | adaptive_icon_foreground_inset: 0 124 | -------------------------------------------------------------------------------- /lib/l10n/generated/app_localizations_zh.dart: -------------------------------------------------------------------------------- 1 | // ignore: unused_import 2 | import 'package:intl/intl.dart' as intl; 3 | import 'app_localizations.dart'; 4 | 5 | // ignore_for_file: type=lint 6 | 7 | /// The translations for Chinese (`zh`). 8 | class AppLocalizationsZh extends AppLocalizations { 9 | AppLocalizationsZh([String locale = 'zh']) : super(locale); 10 | 11 | @override 12 | String get title => '短信清理'; 13 | 14 | @override 15 | String get toast_default => '需要在系统设置中将本应用设置为默认短信应用才能删除短信'; 16 | 17 | @override 18 | String get toast_permission => '需要申请短信权限或设置为短信默认应用'; 19 | 20 | @override 21 | String get keyword => '关键字'; 22 | 23 | @override 24 | String get b_confirm => '确定'; 25 | 26 | @override 27 | String get t_confirm_delete => '确认删除'; 28 | 29 | @override 30 | String delete_num(String num) { 31 | return '是否删除这$num条短信?'; 32 | } 33 | 34 | @override 35 | String get b_cancel => '取消'; 36 | 37 | @override 38 | String get operation_failed => '操作失败'; 39 | 40 | @override 41 | String get operation_completed => '操作成功'; 42 | 43 | @override 44 | String get toast_no => '没有短信可分享'; 45 | 46 | @override 47 | String get sms_list => '短信列表'; 48 | 49 | @override 50 | String get toast_share => '分享成功,需用utf-8格式打开'; 51 | 52 | @override 53 | String get tips => '提示'; 54 | 55 | @override 56 | String get delete_or_move => '是否要删除或移出当前项?'; 57 | 58 | @override 59 | String get b_remove => '移出列表'; 60 | 61 | @override 62 | String get b_delete => '直接删除'; 63 | 64 | @override 65 | String get b_same_number => '同号短信'; 66 | 67 | @override 68 | String get b_same_sim => '同卡短信'; 69 | 70 | @override 71 | String get toast_clipboard => '已复制到剪切板'; 72 | 73 | @override 74 | String get b_copy => '复制'; 75 | 76 | @override 77 | String get sms => '短信'; 78 | 79 | @override 80 | String num_sms(String num) { 81 | return '$num条短信'; 82 | } 83 | 84 | @override 85 | String get t_all_sms => '所有短信列表'; 86 | 87 | @override 88 | String get t_date_filter => '日期范围过滤'; 89 | 90 | @override 91 | String get t_keyword_filter => '关键字过滤'; 92 | 93 | @override 94 | String get set_permission => '申请短信权限'; 95 | 96 | @override 97 | String get set_settings => '应用权限设置'; 98 | 99 | @override 100 | String get set_default => '设置默认短信应用'; 101 | 102 | @override 103 | String get set_restore => '恢复默认短信应用'; 104 | 105 | @override 106 | String get set_export => '导出csv并分享'; 107 | 108 | @override 109 | String get t_wait => '请稍等'; 110 | 111 | @override 112 | String get t_no_sms => '没有短信'; 113 | 114 | @override 115 | String get b_remove_filter => '移除过滤条件'; 116 | 117 | @override 118 | String get t_delete_all => '删除当前列表所有短信'; 119 | 120 | @override 121 | String get sim => '卡'; 122 | 123 | @override 124 | String get t_list_too_long => '列表太多了,可能需要久一点'; 125 | } 126 | 127 | /// The translations for Chinese, as used in Taiwan (`zh_TW`). 128 | class AppLocalizationsZhTw extends AppLocalizationsZh { 129 | AppLocalizationsZhTw() : super('zh_TW'); 130 | 131 | @override 132 | String get title => '簡訊清理'; 133 | 134 | @override 135 | String get toast_default => '需要在系統設定中將本應用程式設定為預設簡訊應用程式才能刪除簡訊'; 136 | 137 | @override 138 | String get toast_permission => '需要申請簡訊權限或設定為預設簡訊應用程式'; 139 | 140 | @override 141 | String get keyword => '關鍵字'; 142 | 143 | @override 144 | String get b_confirm => '確定'; 145 | 146 | @override 147 | String get t_confirm_delete => '確認刪除'; 148 | 149 | @override 150 | String delete_num(String num) { 151 | return '是否刪除這$num則簡訊?'; 152 | } 153 | 154 | @override 155 | String get b_cancel => '取消'; 156 | 157 | @override 158 | String get operation_failed => '操作失敗'; 159 | 160 | @override 161 | String get operation_completed => '操作成功'; 162 | 163 | @override 164 | String get toast_no => '沒有簡訊可分享'; 165 | 166 | @override 167 | String get sms_list => '簡訊清單'; 168 | 169 | @override 170 | String get toast_share => '分享成功,請使用utf-8格式開啟'; 171 | 172 | @override 173 | String get tips => '提示'; 174 | 175 | @override 176 | String get delete_or_move => '是否要刪除或移出目前項目?'; 177 | 178 | @override 179 | String get b_remove => '移出清單'; 180 | 181 | @override 182 | String get b_delete => '直接刪除'; 183 | 184 | @override 185 | String get b_same_number => '相同號碼簡訊'; 186 | 187 | @override 188 | String get b_same_sim => '相同卡簡訊'; 189 | 190 | @override 191 | String get toast_clipboard => '已複製到剪貼簿'; 192 | 193 | @override 194 | String get b_copy => '複製'; 195 | 196 | @override 197 | String get sms => '簡訊'; 198 | 199 | @override 200 | String num_sms(String num) { 201 | return '$num則簡訊'; 202 | } 203 | 204 | @override 205 | String get t_all_sms => '所有簡訊清單'; 206 | 207 | @override 208 | String get t_date_filter => '日期範圍過濾'; 209 | 210 | @override 211 | String get t_keyword_filter => '關鍵字篩選'; 212 | 213 | @override 214 | String get set_permission => '申請簡訊權限'; 215 | 216 | @override 217 | String get set_settings => '應用程式權限設定'; 218 | 219 | @override 220 | String get set_default => '設定為預設簡訊應用程式'; 221 | 222 | @override 223 | String get set_restore => '還原預設簡訊應用程式'; 224 | 225 | @override 226 | String get set_export => '匯出csv並分享'; 227 | 228 | @override 229 | String get t_wait => '請稍候'; 230 | 231 | @override 232 | String get t_no_sms => '沒有簡訊'; 233 | 234 | @override 235 | String get b_remove_filter => '移除篩選條件'; 236 | 237 | @override 238 | String get t_delete_all => '刪除目前清單中的所有簡訊'; 239 | 240 | @override 241 | String get sim => '卡'; 242 | 243 | @override 244 | String get t_list_too_long => '清單較長,可能需要較多時間'; 245 | } 246 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 29 | 33 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 55 | 58 | 59 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /flutter_native_splash.yaml: -------------------------------------------------------------------------------- 1 | flutter_native_splash: 2 | # This package generates native code to customize Flutter's default white native splash screen 3 | # with background color and splash image. 4 | # Customize the parameters below, and run the following command in the terminal: 5 | # dart run flutter_native_splash:create 6 | # To restore Flutter's default white splash screen, run the following command in the terminal: 7 | # dart run flutter_native_splash:remove 8 | 9 | # color or background_image is the only required parameter. Use color to set the background 10 | # of your splash screen to a solid color. Use background_image to set the background of your 11 | # splash screen to a png image. This is useful for gradients. The image will be stretch to the 12 | # size of the app. Only one parameter can be used, color and background_image cannot both be set. 13 | color: "#2AAE67" 14 | #background_image: "assets/background.png" 15 | 16 | # Optional parameters are listed below. To enable a parameter, uncomment the line by removing 17 | # the leading # character. 18 | 19 | # The image parameter allows you to specify an image used in the splash screen. It must be a 20 | # png file and should be sized for 4x pixel density. 21 | #image: assets/splash.png 22 | 23 | # The branding property allows you to specify an image used as branding in the splash screen. 24 | # It must be a png file. It is supported for Android, iOS and the Web. For Android 12, 25 | # see the Android 12 section below. 26 | #branding: assets/dart.png 27 | 28 | # To position the branding image at the bottom of the screen you can use bottom, bottomRight, 29 | # and bottomLeft. The default values is bottom if not specified or specified something else. 30 | #branding_mode: bottom 31 | 32 | # The color_dark, background_image_dark, image_dark, branding_dark are parameters that set the background 33 | # and image when the device is in dark mode. If they are not specified, the app will use the 34 | # parameters from above. If the image_dark parameter is specified, color_dark or 35 | # background_image_dark must be specified. color_dark and background_image_dark cannot both be 36 | # set. 37 | #color_dark: "#042a49" 38 | #background_image_dark: "assets/dark-background.png" 39 | #image_dark: assets/splash-invert.png 40 | #branding_dark: assets/dart_dark.png 41 | 42 | # Android 12 handles the splash screen differently than previous versions. Please visit 43 | # https://developer.android.com/guide/topics/ui/splash-screen 44 | # Following are Android 12 specific parameter. 45 | android_12: 46 | # The image parameter sets the splash screen icon image. If this parameter is not specified, 47 | # the app's launcher icon will be used instead. 48 | # Please note that the splash screen will be clipped to a circle on the center of the screen. 49 | # App icon with an icon background: This should be 960×960 pixels, and fit within a circle 50 | # 640 pixels in diameter. 51 | # App icon without an icon background: This should be 1152×1152 pixels, and fit within a circle 52 | # 768 pixels in diameter. 53 | image: assets/icon/icon.png 54 | 55 | # Splash screen background color. 56 | color: "#2AAE67" 57 | 58 | # App icon background color. 59 | #icon_background_color: "#111111" 60 | 61 | # The branding property allows you to specify an image used as branding in the splash screen. 62 | #branding: assets/dart.png 63 | 64 | # The image_dark, color_dark, icon_background_color_dark, and branding_dark set values that 65 | # apply when the device is in dark mode. If they are not specified, the app will use the 66 | # parameters from above. 67 | #image_dark: assets/android12splash-invert.png 68 | #color_dark: "#042a49" 69 | #icon_background_color_dark: "#eeeeee" 70 | 71 | # The android, ios and web parameters can be used to disable generating a splash screen on a given 72 | # platform. 73 | #android: false 74 | #ios: false 75 | #web: false 76 | 77 | # Platform specific images can be specified with the following parameters, which will override 78 | # the respective parameter. You may specify all, selected, or none of these parameters: 79 | #color_android: "#42a5f5" 80 | #color_dark_android: "#042a49" 81 | #color_ios: "#42a5f5" 82 | #color_dark_ios: "#042a49" 83 | #color_web: "#42a5f5" 84 | #color_dark_web: "#042a49" 85 | #image_android: assets/splash-android.png 86 | #image_dark_android: assets/splash-invert-android.png 87 | #image_ios: assets/splash-ios.png 88 | #image_dark_ios: assets/splash-invert-ios.png 89 | #image_web: assets/splash-web.gif 90 | #image_dark_web: assets/splash-invert-web.gif 91 | #background_image_android: "assets/background-android.png" 92 | #background_image_dark_android: "assets/dark-background-android.png" 93 | #background_image_ios: "assets/background-ios.png" 94 | #background_image_dark_ios: "assets/dark-background-ios.png" 95 | #background_image_web: "assets/background-web.png" 96 | #background_image_dark_web: "assets/dark-background-web.png" 97 | #branding_android: assets/brand-android.png 98 | #branding_dark_android: assets/dart_dark-android.png 99 | #branding_ios: assets/brand-ios.gif 100 | #branding_dark_ios: assets/dart_dark-ios.gif 101 | 102 | # The position of the splash image can be set with android_gravity, ios_content_mode, and 103 | # web_image_mode parameters. All default to center. 104 | # 105 | # android_gravity can be one of the following Android Gravity (see 106 | # https://developer.android.com/reference/android/view/Gravity): bottom, center, 107 | # center_horizontal, center_vertical, clip_horizontal, clip_vertical, end, fill, fill_horizontal, 108 | # fill_vertical, left, right, start, or top. 109 | #android_gravity: center 110 | # 111 | # ios_content_mode can be one of the following iOS UIView.ContentMode (see 112 | # https://developer.apple.com/documentation/uikit/uiview/contentmode): scaleToFill, 113 | # scaleAspectFit, scaleAspectFill, center, top, bottom, left, right, topLeft, topRight, 114 | # bottomLeft, or bottomRight. 115 | #ios_content_mode: center 116 | # 117 | # web_image_mode can be one of the following modes: center, contain, stretch, and cover. 118 | #web_image_mode: center 119 | 120 | # The screen orientation can be set in Android with the android_screen_orientation parameter. 121 | # Valid parameters can be found here: 122 | # https://developer.android.com/guide/topics/manifest/activity-element#screen 123 | #android_screen_orientation: sensorLandscape 124 | 125 | # To hide the notification bar, use the fullscreen parameter. Has no effect in web since web 126 | # has no notification bar. Defaults to false. 127 | # NOTE: Unlike Android, iOS will not automatically show the notification bar when the app loads. 128 | # To show the notification bar, add the following code to your Flutter app: 129 | # WidgetsFlutterBinding.ensureInitialized(); 130 | # SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.bottom, SystemUiOverlay.top], ); 131 | fullscreen: true 132 | 133 | # If you have changed the name(s) of your info.plist file(s), you can specify the filename(s) 134 | # with the info_plist_files parameter. Remove only the # characters in the three lines below, 135 | # do not remove any spaces: 136 | #info_plist_files: 137 | # - 'ios/Runner/Info-Debug.plist' 138 | # - 'ios/Runner/Info-Release.plist' 139 | -------------------------------------------------------------------------------- /lib/l10n/generated/app_localizations.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | import 'package:flutter_localizations/flutter_localizations.dart'; 6 | import 'package:intl/intl.dart' as intl; 7 | 8 | import 'app_localizations_en.dart'; 9 | import 'app_localizations_zh.dart'; 10 | 11 | // ignore_for_file: type=lint 12 | 13 | /// Callers can lookup localized strings with an instance of AppLocalizations 14 | /// returned by `AppLocalizations.of(context)`. 15 | /// 16 | /// Applications need to include `AppLocalizations.delegate()` in their app's 17 | /// `localizationDelegates` list, and the locales they support in the app's 18 | /// `supportedLocales` list. For example: 19 | /// 20 | /// ```dart 21 | /// import 'generated/app_localizations.dart'; 22 | /// 23 | /// return MaterialApp( 24 | /// localizationsDelegates: AppLocalizations.localizationsDelegates, 25 | /// supportedLocales: AppLocalizations.supportedLocales, 26 | /// home: MyApplicationHome(), 27 | /// ); 28 | /// ``` 29 | /// 30 | /// ## Update pubspec.yaml 31 | /// 32 | /// Please make sure to update your pubspec.yaml to include the following 33 | /// packages: 34 | /// 35 | /// ```yaml 36 | /// dependencies: 37 | /// # Internationalization support. 38 | /// flutter_localizations: 39 | /// sdk: flutter 40 | /// intl: any # Use the pinned version from flutter_localizations 41 | /// 42 | /// # Rest of dependencies 43 | /// ``` 44 | /// 45 | /// ## iOS Applications 46 | /// 47 | /// iOS applications define key application metadata, including supported 48 | /// locales, in an Info.plist file that is built into the application bundle. 49 | /// To configure the locales supported by your app, you’ll need to edit this 50 | /// file. 51 | /// 52 | /// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. 53 | /// Then, in the Project Navigator, open the Info.plist file under the Runner 54 | /// project’s Runner folder. 55 | /// 56 | /// Next, select the Information Property List item, select Add Item from the 57 | /// Editor menu, then select Localizations from the pop-up menu. 58 | /// 59 | /// Select and expand the newly-created Localizations item then, for each 60 | /// locale your application supports, add a new item and select the locale 61 | /// you wish to add from the pop-up menu in the Value field. This list should 62 | /// be consistent with the languages listed in the AppLocalizations.supportedLocales 63 | /// property. 64 | abstract class AppLocalizations { 65 | AppLocalizations(String locale) 66 | : localeName = intl.Intl.canonicalizedLocale(locale.toString()); 67 | 68 | final String localeName; 69 | 70 | static AppLocalizations? of(BuildContext context) { 71 | return Localizations.of(context, AppLocalizations); 72 | } 73 | 74 | static const LocalizationsDelegate delegate = 75 | _AppLocalizationsDelegate(); 76 | 77 | /// A list of this localizations delegate along with the default localizations 78 | /// delegates. 79 | /// 80 | /// Returns a list of localizations delegates containing this delegate along with 81 | /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, 82 | /// and GlobalWidgetsLocalizations.delegate. 83 | /// 84 | /// Additional delegates can be added by appending to this list in 85 | /// MaterialApp. This list does not have to be used at all if a custom list 86 | /// of delegates is preferred or required. 87 | static const List> localizationsDelegates = 88 | >[ 89 | delegate, 90 | GlobalMaterialLocalizations.delegate, 91 | GlobalCupertinoLocalizations.delegate, 92 | GlobalWidgetsLocalizations.delegate, 93 | ]; 94 | 95 | /// A list of this localizations delegate's supported locales. 96 | static const List supportedLocales = [ 97 | Locale('en'), 98 | Locale('zh'), 99 | Locale('zh', 'TW'), 100 | ]; 101 | 102 | /// No description provided for @title. 103 | /// 104 | /// In en, this message translates to: 105 | /// **'SMS Cleanup'** 106 | String get title; 107 | 108 | /// No description provided for @toast_default. 109 | /// 110 | /// In en, this message translates to: 111 | /// **'To delete SMS messages, please set this app as the default SMS app in System Settings'** 112 | String get toast_default; 113 | 114 | /// No description provided for @toast_permission. 115 | /// 116 | /// In en, this message translates to: 117 | /// **'SMS permission required or set as default SMS app'** 118 | String get toast_permission; 119 | 120 | /// No description provided for @keyword. 121 | /// 122 | /// In en, this message translates to: 123 | /// **'Keyword'** 124 | String get keyword; 125 | 126 | /// No description provided for @b_confirm. 127 | /// 128 | /// In en, this message translates to: 129 | /// **'Confirm'** 130 | String get b_confirm; 131 | 132 | /// No description provided for @t_confirm_delete. 133 | /// 134 | /// In en, this message translates to: 135 | /// **'Confirm Deletion'** 136 | String get t_confirm_delete; 137 | 138 | /// A message with a single parameter 139 | /// 140 | /// In en, this message translates to: 141 | /// **'Delete {num} message(s)?'** 142 | String delete_num(String num); 143 | 144 | /// No description provided for @b_cancel. 145 | /// 146 | /// In en, this message translates to: 147 | /// **'Cancel'** 148 | String get b_cancel; 149 | 150 | /// No description provided for @operation_failed. 151 | /// 152 | /// In en, this message translates to: 153 | /// **'Operation Failed'** 154 | String get operation_failed; 155 | 156 | /// No description provided for @operation_completed. 157 | /// 158 | /// In en, this message translates to: 159 | /// **'Operation Completed'** 160 | String get operation_completed; 161 | 162 | /// No description provided for @toast_no. 163 | /// 164 | /// In en, this message translates to: 165 | /// **'No messages to share'** 166 | String get toast_no; 167 | 168 | /// No description provided for @sms_list. 169 | /// 170 | /// In en, this message translates to: 171 | /// **'Message List'** 172 | String get sms_list; 173 | 174 | /// No description provided for @toast_share. 175 | /// 176 | /// In en, this message translates to: 177 | /// **'Shared successfully. Please open with UTF-8 encoding'** 178 | String get toast_share; 179 | 180 | /// No description provided for @tips. 181 | /// 182 | /// In en, this message translates to: 183 | /// **'Tips'** 184 | String get tips; 185 | 186 | /// No description provided for @delete_or_move. 187 | /// 188 | /// In en, this message translates to: 189 | /// **'Delete or remove this item?'** 190 | String get delete_or_move; 191 | 192 | /// No description provided for @b_remove. 193 | /// 194 | /// In en, this message translates to: 195 | /// **'Remove from List'** 196 | String get b_remove; 197 | 198 | /// No description provided for @b_delete. 199 | /// 200 | /// In en, this message translates to: 201 | /// **'Delete'** 202 | String get b_delete; 203 | 204 | /// No description provided for @b_same_number. 205 | /// 206 | /// In en, this message translates to: 207 | /// **'Messages from Same Number'** 208 | String get b_same_number; 209 | 210 | /// No description provided for @b_same_sim. 211 | /// 212 | /// In en, this message translates to: 213 | /// **'Messages from Same SIM'** 214 | String get b_same_sim; 215 | 216 | /// No description provided for @toast_clipboard. 217 | /// 218 | /// In en, this message translates to: 219 | /// **'Copied to clipboard'** 220 | String get toast_clipboard; 221 | 222 | /// No description provided for @b_copy. 223 | /// 224 | /// In en, this message translates to: 225 | /// **'Copy'** 226 | String get b_copy; 227 | 228 | /// No description provided for @sms. 229 | /// 230 | /// In en, this message translates to: 231 | /// **'SMS'** 232 | String get sms; 233 | 234 | /// A message with a single parameter 235 | /// 236 | /// In en, this message translates to: 237 | /// **'{num} message(s)'** 238 | String num_sms(String num); 239 | 240 | /// No description provided for @t_all_sms. 241 | /// 242 | /// In en, this message translates to: 243 | /// **'All Messages'** 244 | String get t_all_sms; 245 | 246 | /// No description provided for @t_date_filter. 247 | /// 248 | /// In en, this message translates to: 249 | /// **'Filter by date'** 250 | String get t_date_filter; 251 | 252 | /// No description provided for @t_keyword_filter. 253 | /// 254 | /// In en, this message translates to: 255 | /// **'Filter by Keyword'** 256 | String get t_keyword_filter; 257 | 258 | /// No description provided for @set_permission. 259 | /// 260 | /// In en, this message translates to: 261 | /// **'Request SMS Permission'** 262 | String get set_permission; 263 | 264 | /// No description provided for @set_settings. 265 | /// 266 | /// In en, this message translates to: 267 | /// **'App Settings'** 268 | String get set_settings; 269 | 270 | /// No description provided for @set_default. 271 | /// 272 | /// In en, this message translates to: 273 | /// **'Set as Default SMS App'** 274 | String get set_default; 275 | 276 | /// No description provided for @set_restore. 277 | /// 278 | /// In en, this message translates to: 279 | /// **'Restore Default SMS App'** 280 | String get set_restore; 281 | 282 | /// No description provided for @set_export. 283 | /// 284 | /// In en, this message translates to: 285 | /// **'Export as CSV'** 286 | String get set_export; 287 | 288 | /// No description provided for @t_wait. 289 | /// 290 | /// In en, this message translates to: 291 | /// **'Please wait'** 292 | String get t_wait; 293 | 294 | /// No description provided for @t_no_sms. 295 | /// 296 | /// In en, this message translates to: 297 | /// **'No messages'** 298 | String get t_no_sms; 299 | 300 | /// No description provided for @b_remove_filter. 301 | /// 302 | /// In en, this message translates to: 303 | /// **'Clear Filter'** 304 | String get b_remove_filter; 305 | 306 | /// No description provided for @t_delete_all. 307 | /// 308 | /// In en, this message translates to: 309 | /// **'Delete All Messages in Current List'** 310 | String get t_delete_all; 311 | 312 | /// No description provided for @sim. 313 | /// 314 | /// In en, this message translates to: 315 | /// **'SIM'** 316 | String get sim; 317 | 318 | /// No description provided for @t_list_too_long. 319 | /// 320 | /// In en, this message translates to: 321 | /// **'Large list, may take longer to process'** 322 | String get t_list_too_long; 323 | } 324 | 325 | class _AppLocalizationsDelegate 326 | extends LocalizationsDelegate { 327 | const _AppLocalizationsDelegate(); 328 | 329 | @override 330 | Future load(Locale locale) { 331 | return SynchronousFuture(lookupAppLocalizations(locale)); 332 | } 333 | 334 | @override 335 | bool isSupported(Locale locale) => 336 | ['en', 'zh'].contains(locale.languageCode); 337 | 338 | @override 339 | bool shouldReload(_AppLocalizationsDelegate old) => false; 340 | } 341 | 342 | AppLocalizations lookupAppLocalizations(Locale locale) { 343 | // Lookup logic when language+country codes are specified. 344 | switch (locale.languageCode) { 345 | case 'zh': 346 | { 347 | switch (locale.countryCode) { 348 | case 'TW': 349 | return AppLocalizationsZhTw(); 350 | } 351 | break; 352 | } 353 | } 354 | 355 | // Lookup logic when only language code is specified. 356 | switch (locale.languageCode) { 357 | case 'en': 358 | return AppLocalizationsEn(); 359 | case 'zh': 360 | return AppLocalizationsZh(); 361 | } 362 | 363 | throw FlutterError( 364 | 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' 365 | 'an issue with the localizations generation tool. Please file an issue ' 366 | 'on GitHub with a reproducible sample app and the gen-l10n configuration ' 367 | 'that was used.', 368 | ); 369 | } 370 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | ansicolor: 5 | dependency: transitive 6 | description: 7 | name: ansicolor 8 | sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f" 9 | url: "https://pub.flutter-io.cn" 10 | source: hosted 11 | version: "2.0.3" 12 | archive: 13 | dependency: transitive 14 | description: 15 | name: archive 16 | sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" 17 | url: "https://pub.flutter-io.cn" 18 | source: hosted 19 | version: "4.0.7" 20 | args: 21 | dependency: transitive 22 | description: 23 | name: args 24 | sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 25 | url: "https://pub.flutter-io.cn" 26 | source: hosted 27 | version: "2.7.0" 28 | async: 29 | dependency: transitive 30 | description: 31 | name: async 32 | sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" 33 | url: "https://pub.flutter-io.cn" 34 | source: hosted 35 | version: "2.13.0" 36 | boolean_selector: 37 | dependency: transitive 38 | description: 39 | name: boolean_selector 40 | sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" 41 | url: "https://pub.flutter-io.cn" 42 | source: hosted 43 | version: "2.1.2" 44 | characters: 45 | dependency: transitive 46 | description: 47 | name: characters 48 | sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 49 | url: "https://pub.flutter-io.cn" 50 | source: hosted 51 | version: "1.4.0" 52 | checked_yaml: 53 | dependency: transitive 54 | description: 55 | name: checked_yaml 56 | sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" 57 | url: "https://pub.flutter-io.cn" 58 | source: hosted 59 | version: "2.0.4" 60 | cli_util: 61 | dependency: transitive 62 | description: 63 | name: cli_util 64 | sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c 65 | url: "https://pub.flutter-io.cn" 66 | source: hosted 67 | version: "0.4.2" 68 | clock: 69 | dependency: transitive 70 | description: 71 | name: clock 72 | sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b 73 | url: "https://pub.flutter-io.cn" 74 | source: hosted 75 | version: "1.1.2" 76 | collection: 77 | dependency: transitive 78 | description: 79 | name: collection 80 | sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" 81 | url: "https://pub.flutter-io.cn" 82 | source: hosted 83 | version: "1.19.1" 84 | cross_file: 85 | dependency: "direct main" 86 | description: 87 | name: cross_file 88 | sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" 89 | url: "https://pub.flutter-io.cn" 90 | source: hosted 91 | version: "0.3.4+2" 92 | crypto: 93 | dependency: transitive 94 | description: 95 | name: crypto 96 | sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" 97 | url: "https://pub.flutter-io.cn" 98 | source: hosted 99 | version: "3.0.6" 100 | csslib: 101 | dependency: transitive 102 | description: 103 | name: csslib 104 | sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" 105 | url: "https://pub.flutter-io.cn" 106 | source: hosted 107 | version: "1.0.2" 108 | csv: 109 | dependency: "direct main" 110 | description: 111 | name: csv 112 | sha256: c6aa2679b2a18cb57652920f674488d89712efaf4d3fdf2e537215b35fc19d6c 113 | url: "https://pub.flutter-io.cn" 114 | source: hosted 115 | version: "6.0.0" 116 | fake_async: 117 | dependency: transitive 118 | description: 119 | name: fake_async 120 | sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" 121 | url: "https://pub.flutter-io.cn" 122 | source: hosted 123 | version: "1.3.3" 124 | ffi: 125 | dependency: transitive 126 | description: 127 | name: ffi 128 | sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" 129 | url: "https://pub.flutter-io.cn" 130 | source: hosted 131 | version: "2.1.4" 132 | file: 133 | dependency: transitive 134 | description: 135 | name: file 136 | sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 137 | url: "https://pub.flutter-io.cn" 138 | source: hosted 139 | version: "7.0.1" 140 | fixnum: 141 | dependency: transitive 142 | description: 143 | name: fixnum 144 | sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be 145 | url: "https://pub.flutter-io.cn" 146 | source: hosted 147 | version: "1.1.1" 148 | flutter: 149 | dependency: "direct main" 150 | description: flutter 151 | source: sdk 152 | version: "0.0.0" 153 | flutter_launcher_icons: 154 | dependency: "direct dev" 155 | description: 156 | name: flutter_launcher_icons 157 | sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7" 158 | url: "https://pub.flutter-io.cn" 159 | source: hosted 160 | version: "0.14.4" 161 | flutter_lints: 162 | dependency: "direct dev" 163 | description: 164 | name: flutter_lints 165 | sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" 166 | url: "https://pub.flutter-io.cn" 167 | source: hosted 168 | version: "6.0.0" 169 | flutter_localizations: 170 | dependency: "direct main" 171 | description: flutter 172 | source: sdk 173 | version: "0.0.0" 174 | flutter_native_splash: 175 | dependency: "direct dev" 176 | description: 177 | name: flutter_native_splash 178 | sha256: "8321a6d11a8d13977fa780c89de8d257cce3d841eecfb7a4cadffcc4f12d82dc" 179 | url: "https://pub.flutter-io.cn" 180 | source: hosted 181 | version: "2.4.6" 182 | flutter_smart_dialog: 183 | dependency: "direct main" 184 | description: 185 | name: flutter_smart_dialog 186 | sha256: "0852df132cb03fd8fc5144eb404c31eb7eb50c22aecb1cc2504f2f598090d756" 187 | url: "https://pub.flutter-io.cn" 188 | source: hosted 189 | version: "4.9.8+9" 190 | flutter_test: 191 | dependency: "direct dev" 192 | description: flutter 193 | source: sdk 194 | version: "0.0.0" 195 | flutter_web_plugins: 196 | dependency: transitive 197 | description: flutter 198 | source: sdk 199 | version: "0.0.0" 200 | html: 201 | dependency: transitive 202 | description: 203 | name: html 204 | sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" 205 | url: "https://pub.flutter-io.cn" 206 | source: hosted 207 | version: "0.15.6" 208 | image: 209 | dependency: transitive 210 | description: 211 | name: image 212 | sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" 213 | url: "https://pub.flutter-io.cn" 214 | source: hosted 215 | version: "4.5.4" 216 | intl: 217 | dependency: "direct main" 218 | description: 219 | name: intl 220 | sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" 221 | url: "https://pub.flutter-io.cn" 222 | source: hosted 223 | version: "0.20.2" 224 | json_annotation: 225 | dependency: transitive 226 | description: 227 | name: json_annotation 228 | sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" 229 | url: "https://pub.flutter-io.cn" 230 | source: hosted 231 | version: "4.9.0" 232 | leak_tracker: 233 | dependency: transitive 234 | description: 235 | name: leak_tracker 236 | sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" 237 | url: "https://pub.flutter-io.cn" 238 | source: hosted 239 | version: "11.0.1" 240 | leak_tracker_flutter_testing: 241 | dependency: transitive 242 | description: 243 | name: leak_tracker_flutter_testing 244 | sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" 245 | url: "https://pub.flutter-io.cn" 246 | source: hosted 247 | version: "3.0.10" 248 | leak_tracker_testing: 249 | dependency: transitive 250 | description: 251 | name: leak_tracker_testing 252 | sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" 253 | url: "https://pub.flutter-io.cn" 254 | source: hosted 255 | version: "3.0.2" 256 | lints: 257 | dependency: transitive 258 | description: 259 | name: lints 260 | sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 261 | url: "https://pub.flutter-io.cn" 262 | source: hosted 263 | version: "6.0.0" 264 | matcher: 265 | dependency: transitive 266 | description: 267 | name: matcher 268 | sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 269 | url: "https://pub.flutter-io.cn" 270 | source: hosted 271 | version: "0.12.17" 272 | material_color_utilities: 273 | dependency: transitive 274 | description: 275 | name: material_color_utilities 276 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 277 | url: "https://pub.flutter-io.cn" 278 | source: hosted 279 | version: "0.11.1" 280 | meta: 281 | dependency: transitive 282 | description: 283 | name: meta 284 | sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c 285 | url: "https://pub.flutter-io.cn" 286 | source: hosted 287 | version: "1.16.0" 288 | mime: 289 | dependency: transitive 290 | description: 291 | name: mime 292 | sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" 293 | url: "https://pub.flutter-io.cn" 294 | source: hosted 295 | version: "2.0.0" 296 | path: 297 | dependency: transitive 298 | description: 299 | name: path 300 | sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" 301 | url: "https://pub.flutter-io.cn" 302 | source: hosted 303 | version: "1.9.1" 304 | path_provider: 305 | dependency: "direct main" 306 | description: 307 | name: path_provider 308 | sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" 309 | url: "https://pub.flutter-io.cn" 310 | source: hosted 311 | version: "2.1.5" 312 | path_provider_android: 313 | dependency: transitive 314 | description: 315 | name: path_provider_android 316 | sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 317 | url: "https://pub.flutter-io.cn" 318 | source: hosted 319 | version: "2.2.17" 320 | path_provider_foundation: 321 | dependency: transitive 322 | description: 323 | name: path_provider_foundation 324 | sha256: "16eef174aacb07e09c351502740fa6254c165757638eba1e9116b0a781201bbd" 325 | url: "https://pub.flutter-io.cn" 326 | source: hosted 327 | version: "2.4.2" 328 | path_provider_linux: 329 | dependency: transitive 330 | description: 331 | name: path_provider_linux 332 | sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 333 | url: "https://pub.flutter-io.cn" 334 | source: hosted 335 | version: "2.2.1" 336 | path_provider_platform_interface: 337 | dependency: transitive 338 | description: 339 | name: path_provider_platform_interface 340 | sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" 341 | url: "https://pub.flutter-io.cn" 342 | source: hosted 343 | version: "2.1.2" 344 | path_provider_windows: 345 | dependency: transitive 346 | description: 347 | name: path_provider_windows 348 | sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 349 | url: "https://pub.flutter-io.cn" 350 | source: hosted 351 | version: "2.3.0" 352 | permission_handler: 353 | dependency: "direct main" 354 | description: 355 | name: permission_handler 356 | sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1 357 | url: "https://pub.flutter-io.cn" 358 | source: hosted 359 | version: "12.0.1" 360 | permission_handler_android: 361 | dependency: transitive 362 | description: 363 | name: permission_handler_android 364 | sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6" 365 | url: "https://pub.flutter-io.cn" 366 | source: hosted 367 | version: "13.0.1" 368 | permission_handler_apple: 369 | dependency: transitive 370 | description: 371 | name: permission_handler_apple 372 | sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023 373 | url: "https://pub.flutter-io.cn" 374 | source: hosted 375 | version: "9.4.7" 376 | permission_handler_html: 377 | dependency: transitive 378 | description: 379 | name: permission_handler_html 380 | sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" 381 | url: "https://pub.flutter-io.cn" 382 | source: hosted 383 | version: "0.1.3+5" 384 | permission_handler_platform_interface: 385 | dependency: transitive 386 | description: 387 | name: permission_handler_platform_interface 388 | sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 389 | url: "https://pub.flutter-io.cn" 390 | source: hosted 391 | version: "4.3.0" 392 | permission_handler_windows: 393 | dependency: transitive 394 | description: 395 | name: permission_handler_windows 396 | sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" 397 | url: "https://pub.flutter-io.cn" 398 | source: hosted 399 | version: "0.2.1" 400 | petitparser: 401 | dependency: transitive 402 | description: 403 | name: petitparser 404 | sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" 405 | url: "https://pub.flutter-io.cn" 406 | source: hosted 407 | version: "7.0.1" 408 | platform: 409 | dependency: transitive 410 | description: 411 | name: platform 412 | sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" 413 | url: "https://pub.flutter-io.cn" 414 | source: hosted 415 | version: "3.1.6" 416 | plugin_platform_interface: 417 | dependency: transitive 418 | description: 419 | name: plugin_platform_interface 420 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" 421 | url: "https://pub.flutter-io.cn" 422 | source: hosted 423 | version: "2.1.8" 424 | posix: 425 | dependency: transitive 426 | description: 427 | name: posix 428 | sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" 429 | url: "https://pub.flutter-io.cn" 430 | source: hosted 431 | version: "6.0.3" 432 | share_plus: 433 | dependency: "direct main" 434 | description: 435 | name: share_plus 436 | sha256: d7dc0630a923883c6328ca31b89aa682bacbf2f8304162d29f7c6aaff03a27a1 437 | url: "https://pub.flutter-io.cn" 438 | source: hosted 439 | version: "11.1.0" 440 | share_plus_platform_interface: 441 | dependency: transitive 442 | description: 443 | name: share_plus_platform_interface 444 | sha256: "88023e53a13429bd65d8e85e11a9b484f49d4c190abbd96c7932b74d6927cc9a" 445 | url: "https://pub.flutter-io.cn" 446 | source: hosted 447 | version: "6.1.0" 448 | sky_engine: 449 | dependency: transitive 450 | description: flutter 451 | source: sdk 452 | version: "0.0.0" 453 | sms_advanced: 454 | dependency: "direct main" 455 | description: 456 | name: sms_advanced 457 | sha256: "9d9ae78a6b692758ede569798f60afe6dc907fd25b03d54d290585a9f2fbf148" 458 | url: "https://pub.flutter-io.cn" 459 | source: hosted 460 | version: "1.1.0" 461 | source_span: 462 | dependency: transitive 463 | description: 464 | name: source_span 465 | sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" 466 | url: "https://pub.flutter-io.cn" 467 | source: hosted 468 | version: "1.10.1" 469 | sprintf: 470 | dependency: transitive 471 | description: 472 | name: sprintf 473 | sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" 474 | url: "https://pub.flutter-io.cn" 475 | source: hosted 476 | version: "7.0.0" 477 | stack_trace: 478 | dependency: transitive 479 | description: 480 | name: stack_trace 481 | sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" 482 | url: "https://pub.flutter-io.cn" 483 | source: hosted 484 | version: "1.12.1" 485 | stream_channel: 486 | dependency: transitive 487 | description: 488 | name: stream_channel 489 | sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" 490 | url: "https://pub.flutter-io.cn" 491 | source: hosted 492 | version: "2.1.4" 493 | string_scanner: 494 | dependency: transitive 495 | description: 496 | name: string_scanner 497 | sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" 498 | url: "https://pub.flutter-io.cn" 499 | source: hosted 500 | version: "1.4.1" 501 | term_glyph: 502 | dependency: transitive 503 | description: 504 | name: term_glyph 505 | sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" 506 | url: "https://pub.flutter-io.cn" 507 | source: hosted 508 | version: "1.2.2" 509 | test_api: 510 | dependency: transitive 511 | description: 512 | name: test_api 513 | sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" 514 | url: "https://pub.flutter-io.cn" 515 | source: hosted 516 | version: "0.7.6" 517 | typed_data: 518 | dependency: transitive 519 | description: 520 | name: typed_data 521 | sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 522 | url: "https://pub.flutter-io.cn" 523 | source: hosted 524 | version: "1.4.0" 525 | universal_io: 526 | dependency: transitive 527 | description: 528 | name: universal_io 529 | sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" 530 | url: "https://pub.flutter-io.cn" 531 | source: hosted 532 | version: "2.2.2" 533 | url_launcher: 534 | dependency: transitive 535 | description: 536 | name: url_launcher 537 | sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 538 | url: "https://pub.flutter-io.cn" 539 | source: hosted 540 | version: "6.3.2" 541 | url_launcher_android: 542 | dependency: transitive 543 | description: 544 | name: url_launcher_android 545 | sha256: "0aedad096a85b49df2e4725fa32118f9fa580f3b14af7a2d2221896a02cd5656" 546 | url: "https://pub.flutter-io.cn" 547 | source: hosted 548 | version: "6.3.17" 549 | url_launcher_ios: 550 | dependency: transitive 551 | description: 552 | name: url_launcher_ios 553 | sha256: d80b3f567a617cb923546034cc94bfe44eb15f989fe670b37f26abdb9d939cb7 554 | url: "https://pub.flutter-io.cn" 555 | source: hosted 556 | version: "6.3.4" 557 | url_launcher_linux: 558 | dependency: transitive 559 | description: 560 | name: url_launcher_linux 561 | sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" 562 | url: "https://pub.flutter-io.cn" 563 | source: hosted 564 | version: "3.2.1" 565 | url_launcher_macos: 566 | dependency: transitive 567 | description: 568 | name: url_launcher_macos 569 | sha256: c043a77d6600ac9c38300567f33ef12b0ef4f4783a2c1f00231d2b1941fea13f 570 | url: "https://pub.flutter-io.cn" 571 | source: hosted 572 | version: "3.2.3" 573 | url_launcher_platform_interface: 574 | dependency: transitive 575 | description: 576 | name: url_launcher_platform_interface 577 | sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" 578 | url: "https://pub.flutter-io.cn" 579 | source: hosted 580 | version: "2.3.2" 581 | url_launcher_web: 582 | dependency: transitive 583 | description: 584 | name: url_launcher_web 585 | sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" 586 | url: "https://pub.flutter-io.cn" 587 | source: hosted 588 | version: "2.4.1" 589 | url_launcher_windows: 590 | dependency: transitive 591 | description: 592 | name: url_launcher_windows 593 | sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" 594 | url: "https://pub.flutter-io.cn" 595 | source: hosted 596 | version: "3.1.4" 597 | uuid: 598 | dependency: transitive 599 | description: 600 | name: uuid 601 | sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff 602 | url: "https://pub.flutter-io.cn" 603 | source: hosted 604 | version: "4.5.1" 605 | vector_math: 606 | dependency: transitive 607 | description: 608 | name: vector_math 609 | sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b 610 | url: "https://pub.flutter-io.cn" 611 | source: hosted 612 | version: "2.2.0" 613 | vm_service: 614 | dependency: transitive 615 | description: 616 | name: vm_service 617 | sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" 618 | url: "https://pub.flutter-io.cn" 619 | source: hosted 620 | version: "15.0.2" 621 | web: 622 | dependency: transitive 623 | description: 624 | name: web 625 | sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" 626 | url: "https://pub.flutter-io.cn" 627 | source: hosted 628 | version: "1.1.1" 629 | win32: 630 | dependency: transitive 631 | description: 632 | name: win32 633 | sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" 634 | url: "https://pub.flutter-io.cn" 635 | source: hosted 636 | version: "5.14.0" 637 | xdg_directories: 638 | dependency: transitive 639 | description: 640 | name: xdg_directories 641 | sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" 642 | url: "https://pub.flutter-io.cn" 643 | source: hosted 644 | version: "1.1.0" 645 | xml: 646 | dependency: transitive 647 | description: 648 | name: xml 649 | sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" 650 | url: "https://pub.flutter-io.cn" 651 | source: hosted 652 | version: "6.6.1" 653 | yaml: 654 | dependency: transitive 655 | description: 656 | name: yaml 657 | sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce 658 | url: "https://pub.flutter-io.cn" 659 | source: hosted 660 | version: "3.1.3" 661 | sdks: 662 | dart: ">=3.9.0 <4.0.0" 663 | flutter: ">=3.29.0" 664 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:csv/csv.dart'; 5 | import 'package:flutter/cupertino.dart'; 6 | import 'package:flutter/foundation.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter/services.dart'; 9 | import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; 10 | import 'package:path_provider/path_provider.dart'; 11 | import 'package:permission_handler/permission_handler.dart'; 12 | import 'package:share_plus/share_plus.dart'; 13 | import 'package:sms_advanced/sms_advanced.dart'; 14 | 15 | import 'l10n/generated/app_localizations.dart'; 16 | 17 | void main() { 18 | runApp(const SmsApp()); 19 | } 20 | 21 | class SmsApp extends StatelessWidget { 22 | const SmsApp({super.key}); 23 | 24 | static const Color themeColor = Color(0xFF2BAE67); 25 | 26 | // This widget is the root of your application. 27 | @override 28 | Widget build(BuildContext context) { 29 | return MaterialApp( 30 | onGenerateTitle: (BuildContext context) { 31 | return AppLocalizations.of(context)!.title; 32 | }, 33 | theme: ThemeData( 34 | colorScheme: ColorScheme.fromSeed( 35 | seedColor: themeColor, 36 | dynamicSchemeVariant: DynamicSchemeVariant.fidelity, 37 | ), 38 | ), 39 | darkTheme: ThemeData( 40 | colorScheme: ColorScheme.fromSeed( 41 | seedColor: themeColor, 42 | brightness: Brightness.dark, 43 | dynamicSchemeVariant: DynamicSchemeVariant.fidelity, 44 | ), 45 | brightness: Brightness.dark, 46 | ), 47 | home: const SmsHomePage(), 48 | navigatorObservers: [FlutterSmartDialog.observer], 49 | builder: FlutterSmartDialog.init(), 50 | localizationsDelegates: AppLocalizations.localizationsDelegates, 51 | supportedLocales: AppLocalizations.supportedLocales, 52 | ); 53 | } 54 | } 55 | 56 | class SmsHomePage extends StatefulWidget { 57 | const SmsHomePage({super.key}); 58 | 59 | @override 60 | State createState() => _SmsHomePageState(); 61 | } 62 | 63 | class _SmsHomePageState extends State { 64 | static const _platform = MethodChannel('com.dc16.sms/smsApp'); 65 | static const String _packageId = 'com.dc16.sms'; 66 | final GlobalKey _listKey = GlobalKey(); 67 | final ValueNotifier> _showList = 68 | ValueNotifier>([]); 69 | final TextEditingController _textController = TextEditingController(); 70 | final FocusNode _focusNode = FocusNode(); 71 | final ValueNotifier _showLoading = ValueNotifier(true); 72 | late AppLocalizations appLocalizations; 73 | DateTime? _startDate; 74 | DateTime? _endDate; 75 | 76 | Future _showToast(String msg) async { 77 | SmartDialog.showToast( 78 | msg, 79 | animationType: SmartAnimationType.centerScale_otherSlide, 80 | builder: (_) { 81 | return Container( 82 | margin: const EdgeInsets.symmetric(horizontal: 40, vertical: 100), 83 | padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 20), 84 | decoration: BoxDecoration( 85 | color: 86 | Theme.of(context).brightness == Brightness.light 87 | ? Colors.black 88 | : Colors.grey, 89 | borderRadius: BorderRadius.circular(15), 90 | ), 91 | child: Text( 92 | msg, 93 | style: TextStyle( 94 | color: Colors.white, 95 | fontSize: Theme.of(context).textTheme.titleLarge?.fontSize, 96 | ), 97 | ), 98 | ); 99 | }, 100 | ); 101 | } 102 | 103 | Future _checkDefaultSmsApp() async { 104 | try { 105 | final smsApp = await _platform.invokeMethod('getDefaultSmsApp'); 106 | bool same = (smsApp == _packageId); 107 | if (!same) { 108 | _showToast(appLocalizations.toast_default); 109 | } 110 | return same; 111 | } on PlatformException catch (e) { 112 | debugPrint(e.message); 113 | } 114 | return true; 115 | } 116 | 117 | Future _querySms() async { 118 | bool ok = await Permission.sms.isGranted; 119 | List showMessageList = []; 120 | if (ok) { 121 | _showLoading.value = true; 122 | List allMessageList = []; 123 | SmsQuery query = SmsQuery(); 124 | allMessageList = await query.getAllSms; 125 | if (_textController.text.isNotEmpty) { 126 | for (int i = 0; i < allMessageList.length; ++i) { 127 | if (allMessageList[i].body!.contains(_textController.text)) { 128 | showMessageList.add(allMessageList[i]); 129 | } 130 | } 131 | } else { 132 | showMessageList = allMessageList; 133 | } 134 | 135 | if (_startDate != null && _endDate != null) { 136 | showMessageList = 137 | showMessageList.where((element) { 138 | return element.date!.isAfter(_startDate!) && 139 | element.date!.isBefore(_endDate!); 140 | }).toList(); 141 | } 142 | 143 | showMessageList.sort((a, b) => b.date!.compareTo(a.date!)); 144 | 145 | _showLoading.value = false; 146 | } else { 147 | showMessageList = []; 148 | _showToast(appLocalizations.toast_permission); 149 | _showLoading.value = false; 150 | } 151 | _showList.value = showMessageList; 152 | } 153 | 154 | void _removeIndex(int index) { 155 | final removedItem = _showList.value.removeAt(index); 156 | _showList.value = [..._showList.value]; 157 | _listKey.currentState!.removeItem(index, ( 158 | BuildContext context, 159 | Animation animation, 160 | ) { 161 | return _buildItem(index, removedItem, context, animation); 162 | }); 163 | // return removedItem; 164 | } 165 | 166 | Future _deleteIndex(int index) async { 167 | bool check = await _checkDefaultSmsApp(); 168 | if (check) { 169 | SmsRemover smsRemover = SmsRemover(); 170 | bool? ok = await smsRemover.removeSmsById( 171 | _showList.value[index].id!, 172 | _showList.value[index].threadId!, 173 | ); 174 | if (ok != null) { 175 | if (ok) { 176 | _removeIndex(index); 177 | } else { 178 | _showToast(appLocalizations.operation_failed); 179 | } 180 | } 181 | } 182 | } 183 | 184 | Future _sameAddress(int index) async { 185 | _textController.text = ''; 186 | List showMessageList = []; 187 | bool ok = await Permission.sms.isGranted; 188 | if (ok) { 189 | _showLoading.value = true; 190 | 191 | SmsQuery query = SmsQuery(); 192 | showMessageList = await query.querySms( 193 | address: _showList.value[index].address, 194 | ); 195 | showMessageList.sort((a, b) => b.date!.compareTo(a.date!)); 196 | 197 | _showLoading.value = false; 198 | } else { 199 | showMessageList = []; 200 | } 201 | 202 | _showList.value = showMessageList; 203 | } 204 | 205 | Future _sameSim(int index) async { 206 | _textController.text = ''; 207 | List showMessageList = []; 208 | bool ok = await Permission.sms.isGranted; 209 | if (ok) { 210 | _showLoading.value = true; 211 | 212 | List allMessageList = []; 213 | SmsQuery query = SmsQuery(); 214 | allMessageList = await query.getAllSms; 215 | 216 | int? sim = _showList.value[index].sim; 217 | 218 | if (sim != null) { 219 | for (int i = 0; i < allMessageList.length; ++i) { 220 | if (sim == allMessageList[i].sim) { 221 | showMessageList.add(allMessageList[i]); 222 | } 223 | } 224 | } else { 225 | showMessageList = allMessageList; 226 | } 227 | 228 | showMessageList.sort((a, b) => b.date!.compareTo(a.date!)); 229 | 230 | _showLoading.value = false; 231 | } else { 232 | showMessageList = []; 233 | } 234 | 235 | _showList.value = showMessageList; 236 | } 237 | 238 | void _filterDate() async { 239 | final DateTimeRange? picked = await showDateRangePicker( 240 | context: context, 241 | firstDate: DateTime(1900), 242 | lastDate: DateTime(2999), 243 | initialDateRange: DateTimeRange( 244 | start: _startDate ?? DateTime.now().subtract(Duration(days: 7)), 245 | end: _endDate ?? DateTime.now(), 246 | ), 247 | ); 248 | 249 | if (picked != null) { 250 | _startDate = picked.start; 251 | _endDate = picked.end.add(Duration(hours: 23, minutes: 59, seconds: 59)); 252 | _querySms(); 253 | } 254 | } 255 | 256 | void _filterMsg() { 257 | double top = MediaQuery.of(context).padding.top; 258 | double width = MediaQuery.of(context).size.width / 2; 259 | _focusNode.requestFocus(); 260 | SmartDialog.show( 261 | alignment: Alignment.topCenter, 262 | builder: (_) { 263 | return Container( 264 | padding: const EdgeInsets.all(20), 265 | decoration: BoxDecoration( 266 | color: Theme.of(context).colorScheme.surfaceContainer, 267 | borderRadius: const BorderRadius.vertical( 268 | bottom: Radius.circular(20), 269 | ), 270 | ), 271 | child: Column( 272 | mainAxisSize: MainAxisSize.min, 273 | children: [ 274 | SizedBox(height: top), 275 | TextField( 276 | autofocus: true, 277 | controller: _textController, 278 | focusNode: _focusNode, 279 | decoration: InputDecoration( 280 | labelText: appLocalizations.keyword, 281 | prefixIcon: const Icon(Icons.search_outlined), 282 | suffixIcon: GestureDetector( 283 | onTap: () { 284 | if (_textController.text.isNotEmpty) { 285 | _textController.text = ''; 286 | } else { 287 | SmartDialog.dismiss(status: SmartStatus.custom); 288 | } 289 | }, 290 | child: const Icon(Icons.clear_outlined), 291 | ), 292 | ), 293 | onSubmitted: (_) { 294 | _filterSubmit(); 295 | }, 296 | ), 297 | const SizedBox(height: 20), 298 | FilledButton( 299 | onPressed: _filterSubmit, 300 | child: SizedBox( 301 | width: width, 302 | child: Center(child: Text(appLocalizations.b_confirm)), 303 | ), 304 | ), 305 | ], 306 | ), 307 | ); 308 | }, 309 | ); 310 | } 311 | 312 | void _filterSubmit() { 313 | SmartDialog.dismiss(status: SmartStatus.custom); 314 | _querySms(); 315 | } 316 | 317 | void _deleteMsg() { 318 | showCupertinoModalPopup( 319 | context: context, 320 | builder: (context) { 321 | return CupertinoActionSheet( 322 | title: Text(appLocalizations.t_confirm_delete), 323 | message: Text( 324 | appLocalizations.delete_num(_showList.value.length.toString()), 325 | ), 326 | actions: [ 327 | CupertinoActionSheetAction( 328 | onPressed: () { 329 | Navigator.of(context).pop('delete'); 330 | _deleteSubmit(); 331 | }, 332 | isDestructiveAction: true, 333 | isDefaultAction: true, 334 | child: Text(appLocalizations.b_confirm), 335 | ), 336 | ], 337 | cancelButton: CupertinoActionSheetAction( 338 | child: Text(appLocalizations.b_cancel), 339 | onPressed: () { 340 | Navigator.of(context).pop('cancel'); 341 | }, 342 | ), 343 | ); 344 | }, 345 | ); 346 | } 347 | 348 | Future _deleteSubmit() async { 349 | bool check = await _checkDefaultSmsApp(); 350 | if (check) { 351 | if (_showList.value.length > 3000) { 352 | _showToast(appLocalizations.t_list_too_long); 353 | } 354 | _showLoading.value = true; 355 | SmsRemover smsRemover = SmsRemover(); 356 | for (int i = 0; i < _showList.value.length; ++i) { 357 | await smsRemover.removeSmsById( 358 | _showList.value[i].id!, 359 | _showList.value[i].threadId!, 360 | ); 361 | } 362 | _querySms(); 363 | } 364 | } 365 | 366 | Future _requestPermission() async { 367 | PermissionStatus status = await Permission.sms.request(); 368 | bool ok = (status == PermissionStatus.granted); 369 | if (ok && _showList.value.isEmpty) { 370 | _querySms(); 371 | } 372 | _showToast( 373 | ok 374 | ? appLocalizations.operation_completed 375 | : appLocalizations.operation_failed, 376 | ); 377 | } 378 | 379 | Future _setAppPermission() async { 380 | bool ok = await openAppSettings(); 381 | if (!ok) { 382 | _showToast(appLocalizations.operation_failed); 383 | } 384 | } 385 | 386 | Future _setDefaultApp() async { 387 | try { 388 | final set = await _platform.invokeMethod('setDefaultSmsApp'); 389 | final get = await _platform.invokeMethod('getDefaultSmsApp'); 390 | if (set == 'had' || get == _packageId) { 391 | _showToast(appLocalizations.operation_completed); 392 | } 393 | } on PlatformException catch (e) { 394 | _showToast(e.message ?? appLocalizations.operation_failed); 395 | } 396 | } 397 | 398 | Future _resetDefaultSmsApp() async { 399 | try { 400 | final result = await _platform.invokeMethod('resetDefaultSmsApp'); 401 | if (result == 'no') { 402 | _showToast(appLocalizations.operation_failed); 403 | } 404 | } on PlatformException catch (e) { 405 | _showToast(e.message ?? appLocalizations.operation_failed); 406 | } 407 | } 408 | 409 | Future _delDir(FileSystemEntity file) async { 410 | if (file is Directory) { 411 | final List children = file.listSync(); 412 | for (final FileSystemEntity child in children) { 413 | await _delDir(child); 414 | } 415 | } 416 | await file.delete(); 417 | } 418 | 419 | Future _export() async { 420 | if (_showList.value.isEmpty) { 421 | _showToast(appLocalizations.toast_no); 422 | return; 423 | } 424 | 425 | List headerRow = [ 426 | 'id', 427 | 'threadId', 428 | 'sim', 429 | 'address', 430 | 'body', 431 | 'read', 432 | 'date', 433 | 'dateSent', 434 | 'kind', 435 | 'state', 436 | ]; 437 | List> headerAndDataList = []; 438 | headerAndDataList.add(headerRow); 439 | for (SmsMessage m in _showList.value) { 440 | List dataRow = [ 441 | m.id.toString(), 442 | m.threadId.toString(), 443 | m.sim.toString(), 444 | m.address.toString(), 445 | m.body.toString(), 446 | m.isRead.toString(), 447 | m.date.toString(), 448 | m.dateSent.toString(), 449 | m.kind.toString(), 450 | m.state.toString(), 451 | ]; 452 | headerAndDataList.add(dataRow); 453 | } 454 | 455 | String csvData = const ListToCsvConverter().convert(headerAndDataList); 456 | final bytes = utf8.encode(csvData); 457 | Uint8List data = Uint8List.fromList(bytes); 458 | XFile xFile = XFile.fromData(data, mimeType: 'text/csv'); 459 | Directory tempDir = await getTemporaryDirectory(); 460 | String path = '${tempDir.path}/${appLocalizations.sms_list}.csv'; 461 | xFile.saveTo(path); 462 | final ShareParams params = ShareParams( 463 | text: appLocalizations.sms_list, 464 | files: [XFile(path)], 465 | ); 466 | ShareResult res = await SharePlus.instance.share(params); 467 | if (res.status == ShareResultStatus.success) { 468 | _showToast(appLocalizations.toast_share); 469 | } 470 | 471 | await _delDir(tempDir); 472 | } 473 | 474 | @override 475 | void initState() { 476 | _querySms(); 477 | super.initState(); 478 | } 479 | 480 | Widget _buildItem( 481 | int index, 482 | SmsMessage item, 483 | BuildContext context, 484 | Animation animation, 485 | ) { 486 | return SlideTransition( 487 | position: Tween( 488 | begin: const Offset(1, 0), 489 | end: const Offset(0, 0), 490 | ).animate( 491 | CurvedAnimation( 492 | parent: animation, 493 | curve: Curves.easeInBack, 494 | reverseCurve: Curves.easeInOutBack, 495 | ), 496 | ), 497 | child: Column( 498 | mainAxisSize: MainAxisSize.min, 499 | children: [ 500 | ListTile( 501 | minVerticalPadding: 8, 502 | minLeadingWidth: 4, 503 | title: Column( 504 | crossAxisAlignment: CrossAxisAlignment.start, 505 | children: [Text(item.body ?? ''), const SizedBox(height: 5)], 506 | ), 507 | subtitle: Row( 508 | spacing: 10, 509 | children: [ 510 | Container( 511 | padding: EdgeInsets.symmetric(vertical: 4, horizontal: 8), 512 | decoration: BoxDecoration( 513 | color: Theme.of(context).colorScheme.primaryContainer, 514 | borderRadius: BorderRadius.circular(10), 515 | ), 516 | child: Text( 517 | '${appLocalizations.sim}${item.sim}', 518 | style: TextStyle( 519 | color: Theme.of(context).colorScheme.onPrimary, 520 | ), 521 | ), 522 | ), 523 | Expanded( 524 | child: Text( 525 | item.sender ?? '', 526 | style: TextStyle( 527 | color: Theme.of(context).colorScheme.primary, 528 | ), 529 | overflow: TextOverflow.ellipsis, 530 | ), 531 | ), 532 | Text( 533 | item.date.toString().substring(0, 19), 534 | style: TextStyle( 535 | color: Theme.of(context).colorScheme.primaryContainer, 536 | ), 537 | ), 538 | ], 539 | ), 540 | onTap: () { 541 | showCupertinoModalPopup( 542 | context: context, 543 | builder: (context) { 544 | return CupertinoActionSheet( 545 | title: Text(appLocalizations.tips), 546 | message: Text(appLocalizations.delete_or_move), 547 | actions: [ 548 | CupertinoActionSheetAction( 549 | onPressed: () { 550 | Navigator.of(context).pop('remove'); 551 | _removeIndex(index); 552 | }, 553 | child: Text(appLocalizations.b_remove), 554 | ), 555 | CupertinoActionSheetAction( 556 | onPressed: () { 557 | Navigator.of(context).pop('delete'); 558 | _deleteIndex(index); 559 | }, 560 | isDestructiveAction: true, 561 | isDefaultAction: true, 562 | child: Text(appLocalizations.b_delete), 563 | ), 564 | CupertinoActionSheetAction( 565 | onPressed: () { 566 | Navigator.of(context).pop('same'); 567 | _sameAddress(index); 568 | }, 569 | child: Text(appLocalizations.b_same_number), 570 | ), 571 | CupertinoActionSheetAction( 572 | onPressed: () { 573 | Navigator.of(context).pop('sim'); 574 | _sameSim(index); 575 | }, 576 | child: Text(appLocalizations.b_same_sim), 577 | ), 578 | CupertinoActionSheetAction( 579 | onPressed: () { 580 | Navigator.of(context).pop('copy'); 581 | Clipboard.setData( 582 | ClipboardData( 583 | text: 584 | '${_showList.value[index].address}\r\n${_showList.value[index].date}\r\n${_showList.value[index].body}', 585 | ), 586 | ); 587 | _showToast(appLocalizations.toast_clipboard); 588 | }, 589 | child: Text(appLocalizations.b_copy), 590 | ), 591 | ], 592 | cancelButton: CupertinoActionSheetAction( 593 | child: Text(appLocalizations.b_cancel), 594 | onPressed: () { 595 | Navigator.of(context).pop('cancel'); 596 | }, 597 | ), 598 | ); 599 | }, 600 | ); 601 | }, 602 | onLongPress: () { 603 | showCupertinoModalPopup( 604 | context: context, 605 | builder: (context) { 606 | return CupertinoActionSheet( 607 | title: Padding( 608 | padding: const EdgeInsets.all(20), 609 | child: SelectableText( 610 | item.body ?? '', 611 | style: Theme.of(context).textTheme.titleLarge, 612 | ), 613 | ), 614 | actions: [ 615 | CupertinoActionSheetAction( 616 | onPressed: () { 617 | Navigator.of(context).pop('copy'); 618 | Clipboard.setData( 619 | ClipboardData( 620 | text: '${_showList.value[index].address}', 621 | ), 622 | ); 623 | _showToast(appLocalizations.toast_clipboard); 624 | }, 625 | child: Text(item.address ?? ''), 626 | ), 627 | ], 628 | cancelButton: CupertinoActionSheetAction( 629 | child: Text(appLocalizations.b_cancel), 630 | onPressed: () { 631 | Navigator.of(context).pop('cancel'); 632 | }, 633 | ), 634 | ); 635 | }, 636 | ); 637 | }, 638 | ), 639 | const Padding( 640 | padding: EdgeInsets.symmetric(horizontal: 10), 641 | child: Divider(), 642 | ), 643 | ], 644 | ), 645 | ); 646 | } 647 | 648 | PopupMenuItem _selectView(IconData icon, String text, String id) { 649 | return PopupMenuItem( 650 | value: id, 651 | child: Row( 652 | children: [Icon(icon), const SizedBox(width: 10), Text(text)], 653 | ), 654 | ); 655 | } 656 | 657 | @override 658 | Widget build(BuildContext context) { 659 | SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle( 660 | systemNavigationBarColor: Colors.transparent, 661 | systemNavigationBarIconBrightness: Theme.of(context).brightness, 662 | ); 663 | SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); 664 | SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); 665 | 666 | appLocalizations = AppLocalizations.of(context)!; 667 | 668 | return Scaffold( 669 | appBar: AppBar( 670 | backgroundColor: Theme.of(context).colorScheme.inversePrimary, 671 | title: ValueListenableBuilder( 672 | valueListenable: _showList, 673 | builder: ( 674 | BuildContext context, 675 | List value, 676 | Widget? child, 677 | ) { 678 | return value.isEmpty 679 | ? Text(appLocalizations.sms) 680 | : Text(appLocalizations.num_sms(value.length.toString())); 681 | }, 682 | ), 683 | actions: [ 684 | IconButton( 685 | tooltip: appLocalizations.t_all_sms, 686 | onPressed: () { 687 | _textController.text = ''; 688 | _startDate = null; 689 | _endDate = null; 690 | _querySms(); 691 | }, 692 | icon: const Icon(Icons.format_list_bulleted_outlined), 693 | ), 694 | IconButton( 695 | tooltip: appLocalizations.t_date_filter, 696 | onPressed: _filterDate, 697 | icon: const Icon(Icons.date_range_outlined), 698 | ), 699 | IconButton( 700 | tooltip: appLocalizations.t_keyword_filter, 701 | onPressed: _filterMsg, 702 | icon: const Icon(Icons.search_outlined), 703 | ), 704 | PopupMenuButton( 705 | itemBuilder: 706 | (BuildContext context) => >[ 707 | _selectView( 708 | Icons.message_outlined, 709 | appLocalizations.set_permission, 710 | 'A', 711 | ), 712 | _selectView( 713 | Icons.settings_outlined, 714 | appLocalizations.set_settings, 715 | 'B', 716 | ), 717 | _selectView( 718 | Icons.admin_panel_settings_outlined, 719 | appLocalizations.set_default, 720 | 'C', 721 | ), 722 | _selectView( 723 | Icons.refresh_rounded, 724 | appLocalizations.set_restore, 725 | 'D', 726 | ), 727 | _selectView( 728 | Icons.share_outlined, 729 | appLocalizations.set_export, 730 | 'E', 731 | ), 732 | ], 733 | onSelected: (String action) { 734 | switch (action) { 735 | case 'A': 736 | _requestPermission(); 737 | break; 738 | case 'B': 739 | _setAppPermission(); 740 | break; 741 | case 'C': 742 | _setDefaultApp(); 743 | break; 744 | case 'D': 745 | _resetDefaultSmsApp(); 746 | break; 747 | case 'E': 748 | _export(); 749 | break; 750 | } 751 | }, 752 | ), 753 | ], 754 | ), 755 | body: ValueListenableBuilder( 756 | valueListenable: _showLoading, 757 | builder: (BuildContext context, bool value, Widget? child) { 758 | return value 759 | ? Center( 760 | child: Column( 761 | mainAxisSize: MainAxisSize.min, 762 | children: [ 763 | const CircularProgressIndicator(), 764 | Container( 765 | margin: const EdgeInsets.only(top: 20), 766 | child: Text( 767 | appLocalizations.t_wait, 768 | style: Theme.of(context).textTheme.titleLarge, 769 | ), 770 | ), 771 | ], 772 | ), 773 | ) 774 | : ValueListenableBuilder( 775 | valueListenable: _showList, 776 | builder: ( 777 | BuildContext context, 778 | List value, 779 | Widget? child, 780 | ) { 781 | return value.isEmpty 782 | ? Center( 783 | child: Column( 784 | mainAxisSize: MainAxisSize.min, 785 | children: [ 786 | Icon( 787 | Icons.message_outlined, 788 | size: 80, 789 | color: Theme.of(context).colorScheme.primary, 790 | ), 791 | const SizedBox(height: 10), 792 | Text( 793 | appLocalizations.t_no_sms, 794 | style: Theme.of(context).textTheme.titleLarge, 795 | ), 796 | const SizedBox(height: 80), 797 | FilledButton( 798 | onPressed: () { 799 | _textController.text = ''; 800 | _startDate = null; 801 | _endDate = null; 802 | _querySms(); 803 | }, 804 | child: Text(appLocalizations.b_remove_filter), 805 | ), 806 | const SizedBox(height: 10), 807 | FilledButton( 808 | onPressed: _setDefaultApp, 809 | child: Text(appLocalizations.set_default), 810 | ), 811 | const SizedBox(height: 10), 812 | FilledButton( 813 | onPressed: _requestPermission, 814 | child: Text(appLocalizations.set_permission), 815 | ), 816 | ], 817 | ), 818 | ) 819 | : AnimatedList( 820 | key: _listKey, 821 | initialItemCount: value.length, 822 | itemBuilder: ( 823 | BuildContext context, 824 | int index, 825 | Animation animation, 826 | ) { 827 | SmsMessage item = value[index]; 828 | return _buildItem(index, item, context, animation); 829 | }, 830 | ); 831 | }, 832 | ); 833 | }, 834 | ), 835 | floatingActionButton: FloatingActionButton( 836 | backgroundColor: Theme.of(context).colorScheme.inversePrimary, 837 | tooltip: appLocalizations.t_delete_all, 838 | shape: const CircleBorder(), 839 | onPressed: _deleteMsg, 840 | child: const Icon(Icons.delete_forever_outlined), 841 | ), 842 | ); 843 | } 844 | } 845 | --------------------------------------------------------------------------------