init
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
||||
import '../models/sms_message.dart';
|
||||
import '../main.dart';
|
||||
import '../services/sms_foreground_handler.dart';
|
||||
|
||||
class SmsListScreen extends StatefulWidget {
|
||||
const SmsListScreen({super.key});
|
||||
@@ -24,7 +26,6 @@ class _SmsListScreenState extends State<SmsListScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_uploadedKeys = settings.getUploadedKeys();
|
||||
_requestPermission();
|
||||
}
|
||||
|
||||
@@ -32,16 +33,35 @@ class _SmsListScreenState extends State<SmsListScreen> {
|
||||
final status = await Permission.sms.request();
|
||||
if (mounted) {
|
||||
setState(() => _hasSmsPermission = status.isGranted);
|
||||
if (status.isGranted) _loadSms();
|
||||
if (status.isGranted) {
|
||||
_loadSms();
|
||||
if (settings.autoUpload) _startAutoUpload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadSms() async {
|
||||
setState(() => _loading = true);
|
||||
try {
|
||||
_messages = await smsReaderService.queryAllSms();
|
||||
final local = await smsReaderService.queryAllSms();
|
||||
|
||||
Set<String> serverKeys = {};
|
||||
try {
|
||||
final serverMsgs = await apiService.fetchServerSms();
|
||||
serverKeys = serverMsgs.map((m) {
|
||||
final phone = m['phone_number'] as String? ?? '';
|
||||
final content = m['content'] as String? ?? '';
|
||||
final date = m['sms_date'] as String? ?? '';
|
||||
return '$phone|$content|$date';
|
||||
}).toSet();
|
||||
} catch (_) {}
|
||||
|
||||
setState(() {
|
||||
_messages = local;
|
||||
_uploadedKeys = serverKeys;
|
||||
});
|
||||
} catch (e) { /* ignore */ }
|
||||
setState(() => _loading = false);
|
||||
if (mounted) setState(() => _loading = false);
|
||||
}
|
||||
|
||||
bool _isUploaded(SmsMessage m) => _uploadedKeys.contains(_dedupKey(m));
|
||||
@@ -57,19 +77,20 @@ class _SmsListScreenState extends State<SmsListScreen> {
|
||||
if (toUpload.isEmpty) return;
|
||||
try {
|
||||
await apiService.uploadSms(toUpload);
|
||||
final newKeys = toUpload.map(_dedupKey).toList();
|
||||
settings.addUploadedKeys(newKeys);
|
||||
_uploadedKeys = settings.getUploadedKeys();
|
||||
_selected.clear();
|
||||
final newKeys = toUpload.map(_dedupKey).toSet();
|
||||
setState(() {
|
||||
_uploadedKeys = _uploadedKeys.union(newKeys);
|
||||
_selected.clear();
|
||||
});
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Uploaded ${toUpload.length} messages')),
|
||||
SnackBar(content: Text('已上传 ${toUpload.length} 条短信')),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Upload failed: $e')),
|
||||
SnackBar(content: Text('上传失败: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -80,50 +101,68 @@ class _SmsListScreenState extends State<SmsListScreen> {
|
||||
if (toUpload.isEmpty) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('No new messages to upload')),
|
||||
const SnackBar(content: Text('没有新短信需要上传')),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final count = await apiService.uploadSms(toUpload);
|
||||
final newKeys = toUpload.map(_dedupKey).toList();
|
||||
settings.addUploadedKeys(newKeys);
|
||||
_uploadedKeys = settings.getUploadedKeys();
|
||||
await apiService.uploadSms(toUpload);
|
||||
final newKeys = toUpload.map(_dedupKey).toSet();
|
||||
setState(() {
|
||||
_uploadedKeys = _uploadedKeys.union(newKeys);
|
||||
});
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Uploaded $count messages')),
|
||||
SnackBar(content: Text('已上传 ${toUpload.length} 条短信')),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Upload failed: $e')),
|
||||
SnackBar(content: Text('上传失败: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _toggleAutoUpload() {
|
||||
Future<void> _toggleAutoUpload() async {
|
||||
final newVal = !settings.autoUpload;
|
||||
settings.setAutoUpload(newVal);
|
||||
|
||||
if (newVal) {
|
||||
// Request notification permission for foreground service
|
||||
await Permission.notification.request();
|
||||
_startForegroundService();
|
||||
_startAutoUpload();
|
||||
} else {
|
||||
_stopForegroundService();
|
||||
_smsSub?.cancel();
|
||||
_smsSub = null;
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _startForegroundService() {
|
||||
FlutterForegroundTask.startService(
|
||||
notificationTitle: 'SMS Monitor',
|
||||
notificationText: '正在监听新短信...',
|
||||
callback: startCallback,
|
||||
);
|
||||
}
|
||||
|
||||
void _stopForegroundService() {
|
||||
FlutterForegroundTask.stopService();
|
||||
}
|
||||
|
||||
void _startAutoUpload() {
|
||||
_smsSub = smsReaderService.listenToIncoming().listen((sms) async {
|
||||
try {
|
||||
await apiService.uploadSms([sms]);
|
||||
settings.addUploadedKey(_dedupKey(sms));
|
||||
_uploadedKeys = settings.getUploadedKeys();
|
||||
_messages.insert(0, sms);
|
||||
if (mounted) setState(() {});
|
||||
setState(() {
|
||||
_uploadedKeys.add(_dedupKey(sms));
|
||||
_messages.insert(0, sms);
|
||||
});
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
@@ -140,10 +179,10 @@ class _SmsListScreenState extends State<SmsListScreen> {
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Messages'),
|
||||
title: const Text('短信'),
|
||||
actions: [
|
||||
if (_selected.isNotEmpty)
|
||||
IconButton(icon: const Icon(Icons.cloud_upload), onPressed: _uploadSelected, tooltip: 'Upload selected'),
|
||||
IconButton(icon: const Icon(Icons.cloud_upload), onPressed: _uploadSelected, tooltip: '上传选中'),
|
||||
IconButton(icon: const Icon(Icons.refresh), onPressed: _hasSmsPermission ? _loadSms : _requestPermission),
|
||||
],
|
||||
),
|
||||
@@ -152,16 +191,16 @@ class _SmsListScreenState extends State<SmsListScreen> {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('SMS permission required'),
|
||||
const Text('需要短信权限'),
|
||||
const SizedBox(height: 12),
|
||||
FilledButton(onPressed: _requestPermission, child: const Text('Grant')),
|
||||
FilledButton(onPressed: _requestPermission, child: const Text('授权')),
|
||||
],
|
||||
),
|
||||
)
|
||||
: _loading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: _messages.isEmpty
|
||||
? const Center(child: Text('No messages'))
|
||||
? const Center(child: Text('暂无短信'))
|
||||
: RefreshIndicator(
|
||||
onRefresh: _loadSms,
|
||||
child: ListView.builder(
|
||||
@@ -219,7 +258,7 @@ class _SmsListScreenState extends State<SmsListScreen> {
|
||||
FloatingActionButton(
|
||||
heroTag: 'upload',
|
||||
onPressed: _uploadAll,
|
||||
tooltip: 'Upload all',
|
||||
tooltip: '全部上传',
|
||||
child: const Icon(Icons.cloud_upload),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user