feat: add block management features and sync with system blocklist
This commit is contained in:
parent
ed07022916
commit
2e8f457889
@ -9,7 +9,10 @@ import android.database.Cursor
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.CallLog
|
import android.provider.CallLog
|
||||||
|
import android.provider.BlockedNumberContract
|
||||||
import android.telecom.TelecomManager
|
import android.telecom.TelecomManager
|
||||||
|
import android.telephony.SubscriptionManager
|
||||||
|
import android.telephony.SubscriptionInfo
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.icing.dialer.KeystoreHelper
|
import com.icing.dialer.KeystoreHelper
|
||||||
@ -23,6 +26,7 @@ class MainActivity : FlutterActivity() {
|
|||||||
private val KEYSTORE_CHANNEL = "com.example.keystore"
|
private val KEYSTORE_CHANNEL = "com.example.keystore"
|
||||||
private val CALLLOG_CHANNEL = "com.example.calllog"
|
private val CALLLOG_CHANNEL = "com.example.calllog"
|
||||||
private val CALL_CHANNEL = "call_service"
|
private val CALL_CHANNEL = "call_service"
|
||||||
|
private val BLOCK_CHANNEL = "com.example.block"
|
||||||
private val TAG = "MainActivity"
|
private val TAG = "MainActivity"
|
||||||
private val REQUEST_CODE_SET_DEFAULT_DIALER = 1001
|
private val REQUEST_CODE_SET_DEFAULT_DIALER = 1001
|
||||||
private val REQUEST_CODE_CALL_LOG_PERMISSION = 1002
|
private val REQUEST_CODE_CALL_LOG_PERMISSION = 1002
|
||||||
@ -240,6 +244,44 @@ class MainActivity : FlutterActivity() {
|
|||||||
result.notImplemented()
|
result.notImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, BLOCK_CHANNEL)
|
||||||
|
.setMethodCallHandler { call, result ->
|
||||||
|
when (call.method) {
|
||||||
|
"getBlockedNumbers" -> {
|
||||||
|
val blockedNumbers = getSystemBlockedNumbers()
|
||||||
|
result.success(blockedNumbers)
|
||||||
|
}
|
||||||
|
"blockNumber" -> {
|
||||||
|
val number = call.argument<String>("number")
|
||||||
|
if (number != null) {
|
||||||
|
val success = blockNumberInSystem(number)
|
||||||
|
result.success(success)
|
||||||
|
} else {
|
||||||
|
result.error("INVALID_ARGUMENT", "Number is required", null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"unblockNumber" -> {
|
||||||
|
val number = call.argument<String>("number")
|
||||||
|
if (number != null) {
|
||||||
|
val success = unblockNumberFromSystem(number)
|
||||||
|
result.success(success)
|
||||||
|
} else {
|
||||||
|
result.error("INVALID_ARGUMENT", "Number is required", null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"isNumberBlocked" -> {
|
||||||
|
val number = call.argument<String>("number")
|
||||||
|
if (number != null) {
|
||||||
|
val isBlocked = isNumberBlockedInSystem(number)
|
||||||
|
result.success(isBlocked)
|
||||||
|
} else {
|
||||||
|
result.error("INVALID_ARGUMENT", "Number is required", null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> result.notImplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isDefaultDialer(): Boolean {
|
private fun isDefaultDialer(): Boolean {
|
||||||
@ -354,4 +396,111 @@ class MainActivity : FlutterActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Block management methods
|
||||||
|
private fun getSystemBlockedNumbers(): List<String> {
|
||||||
|
val blockedNumbers = mutableListOf<String>()
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
if (BlockedNumberContract.canCurrentUserBlockNumbers(this)) {
|
||||||
|
val cursor = contentResolver.query(
|
||||||
|
BlockedNumberContract.BlockedNumbers.CONTENT_URI,
|
||||||
|
arrayOf(BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor?.use {
|
||||||
|
while (it.moveToNext()) {
|
||||||
|
val number = it.getString(0)
|
||||||
|
if (number != null) {
|
||||||
|
blockedNumbers.add(number)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "Failed to get system blocked numbers: ${e.message}")
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockedNumbers
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun blockNumberInSystem(number: String): Boolean {
|
||||||
|
return try {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
if (BlockedNumberContract.canCurrentUserBlockNumbers(this)) {
|
||||||
|
val values = android.content.ContentValues().apply {
|
||||||
|
put(BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER, number)
|
||||||
|
}
|
||||||
|
val uri = contentResolver.insert(BlockedNumberContract.BlockedNumbers.CONTENT_URI, values)
|
||||||
|
uri != null
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Current user cannot block numbers")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Block numbers API not available on this Android version")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "Failed to block number $number: ${e.message}")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unblockNumberFromSystem(number: String): Boolean {
|
||||||
|
return try {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
if (BlockedNumberContract.canCurrentUserBlockNumbers(this)) {
|
||||||
|
val deletedRows = contentResolver.delete(
|
||||||
|
BlockedNumberContract.BlockedNumbers.CONTENT_URI,
|
||||||
|
"${BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER} = ?",
|
||||||
|
arrayOf(number)
|
||||||
|
)
|
||||||
|
deletedRows > 0
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Current user cannot unblock numbers")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Block numbers API not available on this Android version")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "Failed to unblock number $number: ${e.message}")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isNumberBlockedInSystem(number: String): Boolean {
|
||||||
|
return try {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
if (BlockedNumberContract.canCurrentUserBlockNumbers(this)) {
|
||||||
|
val cursor = contentResolver.query(
|
||||||
|
BlockedNumberContract.BlockedNumbers.CONTENT_URI,
|
||||||
|
arrayOf(BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER),
|
||||||
|
"${BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER} = ?",
|
||||||
|
arrayOf(number),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
cursor?.use {
|
||||||
|
return it.count > 0
|
||||||
|
}
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "Failed to check if number $number is blocked: ${e.message}")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,9 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
/// Service for managing blocked phone numbers
|
/// Service for managing blocked phone numbers
|
||||||
class BlockService {
|
class BlockService {
|
||||||
static const String _blockedNumbersKey = 'blocked_numbers';
|
static const String _blockedNumbersKey = 'blocked_numbers';
|
||||||
|
static const MethodChannel _channel = MethodChannel('com.example.block');
|
||||||
|
|
||||||
// Private constructor
|
// Private constructor
|
||||||
BlockService._privateConstructor();
|
BlockService._privateConstructor();
|
||||||
@ -16,46 +18,71 @@ class BlockService {
|
|||||||
return _instance;
|
return _instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Block a phone number
|
/// Block a phone number (syncs with system and app)
|
||||||
Future<bool> blockNumber(String phoneNumber) async {
|
Future<bool> blockNumber(String phoneNumber) async {
|
||||||
try {
|
try {
|
||||||
|
// First try to block in system
|
||||||
|
try {
|
||||||
|
await _channel.invokeMethod('blockNumber', {'number': phoneNumber});
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Failed to block in system: $e');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always save to app storage as backup
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final blockedNumbers = prefs.getStringList(_blockedNumbersKey) ?? [];
|
final blockedNumbers = prefs.getStringList(_blockedNumbersKey) ?? [];
|
||||||
|
|
||||||
// Don't add if already blocked
|
// Don't add if already blocked
|
||||||
if (blockedNumbers.contains(phoneNumber)) {
|
if (!blockedNumbers.contains(phoneNumber)) {
|
||||||
return true;
|
blockedNumbers.add(phoneNumber);
|
||||||
|
await prefs.setStringList(_blockedNumbersKey, blockedNumbers);
|
||||||
}
|
}
|
||||||
|
|
||||||
blockedNumbers.add(phoneNumber);
|
return true; // Return true if either system or app blocking succeeded
|
||||||
return await prefs.setStringList(_blockedNumbersKey, blockedNumbers);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Error blocking number: $e');
|
debugPrint('Error blocking number: $e');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unblock a phone number
|
/// Unblock a phone number (syncs with system and app)
|
||||||
Future<bool> unblockNumber(String phoneNumber) async {
|
Future<bool> unblockNumber(String phoneNumber) async {
|
||||||
try {
|
try {
|
||||||
|
// First try to unblock from system
|
||||||
|
try {
|
||||||
|
await _channel.invokeMethod('unblockNumber', {'number': phoneNumber});
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Failed to unblock from system: $e');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always remove from app storage
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final blockedNumbers = prefs.getStringList(_blockedNumbersKey) ?? [];
|
final blockedNumbers = prefs.getStringList(_blockedNumbersKey) ?? [];
|
||||||
|
|
||||||
if (!blockedNumbers.contains(phoneNumber)) {
|
if (blockedNumbers.contains(phoneNumber)) {
|
||||||
return true;
|
blockedNumbers.remove(phoneNumber);
|
||||||
|
await prefs.setStringList(_blockedNumbersKey, blockedNumbers);
|
||||||
}
|
}
|
||||||
|
|
||||||
blockedNumbers.remove(phoneNumber);
|
return true;
|
||||||
return await prefs.setStringList(_blockedNumbersKey, blockedNumbers);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Error unblocking number: $e');
|
debugPrint('Error unblocking number: $e');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a number is blocked
|
/// Check if a number is blocked (checks both system and app)
|
||||||
Future<bool> isNumberBlocked(String phoneNumber) async {
|
Future<bool> isNumberBlocked(String phoneNumber) async {
|
||||||
try {
|
try {
|
||||||
|
// Check system first
|
||||||
|
try {
|
||||||
|
bool systemBlocked = await _channel.invokeMethod('isNumberBlocked', {'number': phoneNumber});
|
||||||
|
if (systemBlocked) return true;
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Failed to check system blocked status: $e');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check app storage as fallback
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final blockedNumbers = prefs.getStringList(_blockedNumbersKey) ?? [];
|
final blockedNumbers = prefs.getStringList(_blockedNumbersKey) ?? [];
|
||||||
return blockedNumbers.contains(phoneNumber);
|
return blockedNumbers.contains(phoneNumber);
|
||||||
@ -65,14 +92,61 @@ class BlockService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all blocked numbers
|
/// Get all blocked numbers (merges system and app blocked numbers)
|
||||||
Future<List<String>> getBlockedNumbers() async {
|
Future<List<String>> getBlockedNumbers() async {
|
||||||
try {
|
try {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
Set<String> allBlockedNumbers = {};
|
||||||
return prefs.getStringList(_blockedNumbersKey) ?? [];
|
|
||||||
|
// Get system blocked numbers
|
||||||
|
try {
|
||||||
|
List<dynamic> systemBlocked = await _channel.invokeMethod('getBlockedNumbers');
|
||||||
|
allBlockedNumbers.addAll(systemBlocked.cast<String>());
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Failed to get system blocked numbers: $e');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get app blocked numbers
|
||||||
|
try {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final appBlocked = prefs.getStringList(_blockedNumbersKey) ?? [];
|
||||||
|
allBlockedNumbers.addAll(appBlocked);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Failed to get app blocked numbers: $e');
|
||||||
|
}
|
||||||
|
|
||||||
|
return allBlockedNumbers.toList()..sort();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('Error getting blocked numbers: $e');
|
debugPrint('Error getting blocked numbers: $e');
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sync app blocked numbers with system blocked numbers
|
||||||
|
Future<void> syncWithSystem() async {
|
||||||
|
try {
|
||||||
|
// Get system blocked numbers
|
||||||
|
List<String> systemBlocked = [];
|
||||||
|
try {
|
||||||
|
List<dynamic> result = await _channel.invokeMethod('getBlockedNumbers');
|
||||||
|
systemBlocked = result.cast<String>();
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Failed to get system blocked numbers for sync: $e');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get app blocked numbers
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
List<String> appBlocked = prefs.getStringList(_blockedNumbersKey) ?? [];
|
||||||
|
|
||||||
|
// Merge them (system takes precedence)
|
||||||
|
Set<String> mergedBlocked = {...systemBlocked, ...appBlocked};
|
||||||
|
|
||||||
|
// Update app storage with merged list
|
||||||
|
await prefs.setStringList(_blockedNumbersKey, mergedBlocked.toList());
|
||||||
|
|
||||||
|
debugPrint('Synced ${mergedBlocked.length} blocked numbers with system');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Error syncing with system: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import '../../../../domain/services/block_service.dart';
|
||||||
|
|
||||||
class BlockedNumbersPage extends StatefulWidget {
|
class BlockedNumbersPage extends StatefulWidget {
|
||||||
const BlockedNumbersPage({super.key});
|
const BlockedNumbersPage({super.key});
|
||||||
@ -12,6 +13,8 @@ class _BlockedNumbersPageState extends State<BlockedNumbersPage> {
|
|||||||
bool _blockUnknownNumbers = false; // Toggle for blocking unknown numbers
|
bool _blockUnknownNumbers = false; // Toggle for blocking unknown numbers
|
||||||
List<String> _blockedNumbers = []; // List of blocked numbers
|
List<String> _blockedNumbers = []; // List of blocked numbers
|
||||||
final TextEditingController _numberController = TextEditingController();
|
final TextEditingController _numberController = TextEditingController();
|
||||||
|
final BlockService _blockService = BlockService();
|
||||||
|
bool _isLoading = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -19,13 +22,33 @@ class _BlockedNumbersPageState extends State<BlockedNumbersPage> {
|
|||||||
_loadPreferences(); // Load data on initialization
|
_loadPreferences(); // Load data on initialization
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load preferences from local storage
|
// Load preferences from local storage and sync with system
|
||||||
Future<void> _loadPreferences() async {
|
Future<void> _loadPreferences() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_blockUnknownNumbers = prefs.getBool('blockUnknownNumbers') ?? false;
|
_isLoading = true;
|
||||||
_blockedNumbers = prefs.getStringList('blockedNumbers') ?? [];
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final blockUnknownNumbers = prefs.getBool('blockUnknownNumbers') ?? false;
|
||||||
|
|
||||||
|
// Sync with system blocked numbers
|
||||||
|
await _blockService.syncWithSystem();
|
||||||
|
|
||||||
|
// Get all blocked numbers (merged from system and app)
|
||||||
|
final blockedNumbers = await _blockService.getBlockedNumbers();
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_blockUnknownNumbers = blockUnknownNumbers;
|
||||||
|
_blockedNumbers = blockedNumbers;
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Error loading preferences: $e');
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save preferences to local storage
|
// Save preferences to local storage
|
||||||
@ -41,84 +64,121 @@ class _BlockedNumbersPageState extends State<BlockedNumbersPage> {
|
|||||||
backgroundColor: Colors.black,
|
backgroundColor: Colors.black,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Blocked Numbers'),
|
title: const Text('Blocked Numbers'),
|
||||||
),
|
actions: [
|
||||||
body: ListView(
|
IconButton(
|
||||||
padding: const EdgeInsets.all(16),
|
icon: const Icon(Icons.sync),
|
||||||
children: [
|
onPressed: _isLoading ? null : () async {
|
||||||
SwitchListTile(
|
await _loadPreferences();
|
||||||
title: const Text(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
'Block Unknown Numbers',
|
const SnackBar(content: Text('Synced with system blocklist')),
|
||||||
style: TextStyle(color: Colors.white),
|
);
|
||||||
),
|
|
||||||
value: _blockUnknownNumbers,
|
|
||||||
onChanged: (bool value) {
|
|
||||||
setState(() {
|
|
||||||
_blockUnknownNumbers = value;
|
|
||||||
_savePreferences(); // Save the state to local storage
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
),
|
tooltip: 'Sync with system blocklist',
|
||||||
const SizedBox(height: 16),
|
|
||||||
ListTile(
|
|
||||||
title: const Text(
|
|
||||||
'Blocked Numbers',
|
|
||||||
style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
subtitle: _blockedNumbers.isEmpty
|
|
||||||
? const Text(
|
|
||||||
'No blocked numbers',
|
|
||||||
style: TextStyle(color: Colors.grey),
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
..._blockedNumbers.map(
|
|
||||||
(number) => ListTile(
|
|
||||||
title: Text(
|
|
||||||
number,
|
|
||||||
style: const TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
trailing: IconButton(
|
|
||||||
icon: const Icon(Icons.delete, color: Colors.red),
|
|
||||||
onPressed: () => _unblockNumber(number),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Divider(color: Colors.grey),
|
|
||||||
ListTile(
|
|
||||||
title: const Text(
|
|
||||||
'Block a Number',
|
|
||||||
style: TextStyle(color: Colors.white),
|
|
||||||
),
|
|
||||||
trailing: const Icon(Icons.add, color: Colors.white),
|
|
||||||
onTap: () => _showBlockNumberDialog(),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
body: _isLoading
|
||||||
|
? const Center(child: CircularProgressIndicator())
|
||||||
|
: ListView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
SwitchListTile(
|
||||||
|
title: const Text(
|
||||||
|
'Block Unknown Numbers',
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
value: _blockUnknownNumbers,
|
||||||
|
onChanged: (bool value) {
|
||||||
|
setState(() {
|
||||||
|
_blockUnknownNumbers = value;
|
||||||
|
_savePreferences(); // Save the state to local storage
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
ListTile(
|
||||||
|
title: const Text(
|
||||||
|
'Blocked Numbers',
|
||||||
|
style: TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
subtitle: _blockedNumbers.isEmpty
|
||||||
|
? const Text(
|
||||||
|
'No blocked numbers',
|
||||||
|
style: TextStyle(color: Colors.grey),
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
'${_blockedNumbers.length} numbers blocked (includes system blocklist)',
|
||||||
|
style: const TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
..._blockedNumbers.map(
|
||||||
|
(number) => ListTile(
|
||||||
|
title: Text(
|
||||||
|
number,
|
||||||
|
style: const TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: const Icon(Icons.delete, color: Colors.red),
|
||||||
|
onPressed: () => _unblockNumber(number),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Divider(color: Colors.grey),
|
||||||
|
ListTile(
|
||||||
|
title: const Text(
|
||||||
|
'Block a Number',
|
||||||
|
style: TextStyle(color: Colors.white),
|
||||||
|
),
|
||||||
|
trailing: const Icon(Icons.add, color: Colors.white),
|
||||||
|
onTap: () => _showBlockNumberDialog(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to block a number
|
// Function to block a number
|
||||||
void _blockNumber(String number) {
|
void _blockNumber(String number) async {
|
||||||
if (number.isNotEmpty && !_blockedNumbers.contains(number)) {
|
if (number.isNotEmpty && !_blockedNumbers.contains(number)) {
|
||||||
setState(() {
|
try {
|
||||||
_blockedNumbers.add(number);
|
final success = await _blockService.blockNumber(number);
|
||||||
_savePreferences(); // Save the updated list
|
if (success) {
|
||||||
});
|
await _loadPreferences(); // Refresh the list
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('$number has been blocked')),
|
SnackBar(content: Text('$number has been blocked')),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Failed to block $number')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Error blocking $number: $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to unblock a number
|
// Function to unblock a number
|
||||||
void _unblockNumber(String number) {
|
void _unblockNumber(String number) async {
|
||||||
setState(() {
|
try {
|
||||||
_blockedNumbers.remove(number);
|
final success = await _blockService.unblockNumber(number);
|
||||||
_savePreferences(); // Save the updated list
|
if (success) {
|
||||||
});
|
await _loadPreferences(); // Refresh the list
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('$number has been unblocked')),
|
SnackBar(content: Text('$number has been unblocked')),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Failed to unblock $number')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Error unblocking $number: $e')),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dialog for blocking a new number
|
// Dialog for blocking a new number
|
||||||
|
Loading…
Reference in New Issue
Block a user