├── 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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------