Compare commits

..

No commits in common. "9bfb55821dca3fa883cbca53cfe52251a871c33e" and "7c7a4f28f4446224efb838132d58e0c94a649ae5" have entirely different histories.

6 changed files with 94 additions and 214 deletions

View File

@ -10,22 +10,8 @@ jobs:
- uses: actions/checkout@v1 - uses: actions/checkout@v1
with: with:
subpath: dialer/ subpath: dialer/
- uses: docker://git.gmoker.com/icing/flutter:main - uses: icing/flutter@main
- uses: actions/upload-artifact@v1 - uses: actions/upload-artifact@v1
with: with:
name: icing-dialer-${{ gitea.ref_name }}-${{ gitea.run_id }}.apk name: icing-dialer-${{ gitea.ref_name }}-${{ gitea.run_id }}.apk
path: build/app/outputs/flutter-apk/app-release.apk path: build/app/outputs/flutter-apk/app-release.apk
build-stealth:
runs-on: debian
steps:
- uses: actions/checkout@v1
with:
subpath: dialer/
- uses: docker://git.gmoker.com/icing/flutter:main
with:
args: "build apk --dart-define=STEALTH=true"
- uses: actions/upload-artifact@v1
with:
name: icing-dialer-stealth-${{ gitea.ref_name }}-${{ gitea.run_id }}.apk
path: build/app/outputs/flutter-apk/app-release.apk

View File

@ -2,9 +2,4 @@
IMG=git.gmoker.com/icing/flutter:main IMG=git.gmoker.com/icing/flutter:main
if [ "$1" == '-s' ]; then docker run --rm -v "$PWD:/app/" "$IMG" build apk
OPT+=(--dart-define=STEALTH=true)
fi
set -x
docker run --rm -v "$PWD:/app/" "$IMG" build apk "${OPT[@]}"

View File

@ -11,7 +11,6 @@ import 'package:dialer/features/contacts/contact_state.dart';
import 'package:dialer/widgets/username_color_generator.dart'; import 'package:dialer/widgets/username_color_generator.dart';
import '../../services/block_service.dart'; import '../../services/block_service.dart';
import '../contacts/widgets/contact_modal.dart'; import '../contacts/widgets/contact_modal.dart';
import '../../services/call_service.dart';
class History { class History {
final Contact contact; final Contact contact;
@ -42,7 +41,6 @@ class _HistoryPageState extends State<HistoryPage>
bool loading = true; bool loading = true;
int? _expandedIndex; int? _expandedIndex;
final ObfuscateService _obfuscateService = ObfuscateService(); final ObfuscateService _obfuscateService = ObfuscateService();
final CallService _callService = CallService();
// Create a MethodChannel instance. // Create a MethodChannel instance.
static const MethodChannel _channel = MethodChannel('com.example.calllog'); static const MethodChannel _channel = MethodChannel('com.example.calllog');
@ -85,8 +83,8 @@ class _HistoryPageState extends State<HistoryPage>
} }
} catch (e) { } catch (e) {
print("Error updating favorite status: $e"); print("Error updating favorite status: $e");
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context)
SnackBar(content: Text('Failed to update favorite status'))); .showSnackBar(SnackBar(content: Text('Failed to update favorite status')));
} }
} }
@ -195,8 +193,7 @@ class _HistoryPageState extends State<HistoryPage>
); );
} }
callHistories callHistories.add(History(matchedContact, callDate, callType, callStatus, 1));
.add(History(matchedContact, callDate, callType, callStatus, 1));
} }
// Sort histories by most recent. // Sort histories by most recent.
@ -363,8 +360,7 @@ class _HistoryPageState extends State<HistoryPage>
onEdit: () async { onEdit: () async {
if (await FlutterContacts.requestPermission()) { if (await FlutterContacts.requestPermission()) {
final updatedContact = final updatedContact =
await FlutterContacts.openExternalEdit( await FlutterContacts.openExternalEdit(contact.id);
contact.id);
if (updatedContact != null) { if (updatedContact != null) {
await _refreshContacts(); await _refreshContacts();
Navigator.of(context).pop(); Navigator.of(context).pop();
@ -419,11 +415,18 @@ class _HistoryPageState extends State<HistoryPage>
icon: const Icon(Icons.phone, color: Colors.green), icon: const Icon(Icons.phone, color: Colors.green),
onPressed: () async { onPressed: () async {
if (contact.phones.isNotEmpty) { if (contact.phones.isNotEmpty) {
_callService.makeGsmCall(contact.phones.first.number); final Uri callUri =
Uri(scheme: 'tel', path: contact.phones.first.number);
if (await canLaunchUrl(callUri)) {
await launchUrl(callUri);
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(content: Text('Could not launch call')),
content: Text('Contact has no phone number')), );
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Contact has no phone number')),
); );
} }
}, },
@ -441,9 +444,7 @@ class _HistoryPageState extends State<HistoryPage>
color: Colors.grey[850], color: Colors.grey[850],
child: FutureBuilder<bool>( child: FutureBuilder<bool>(
future: BlockService().isNumberBlocked( future: BlockService().isNumberBlocked(
contact.phones.isNotEmpty contact.phones.isNotEmpty ? contact.phones.first.number : ''),
? contact.phones.first.number
: ''),
builder: (context, snapshot) { builder: (context, snapshot) {
final isBlocked = snapshot.data ?? false; final isBlocked = snapshot.data ?? false;
return Row( return Row(
@ -459,37 +460,29 @@ class _HistoryPageState extends State<HistoryPage>
await launchUrl(smsUri); await launchUrl(smsUri);
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(content: Text('Could not send message')),
content:
Text('Could not send message')),
); );
} }
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(content: Text('Contact has no phone number')),
content:
Text('Contact has no phone number')),
); );
} }
}, },
icon: icon: const Icon(Icons.message, color: Colors.white),
const Icon(Icons.message, color: Colors.white), label: const Text('Message', style: TextStyle(color: Colors.white)),
label: const Text('Message',
style: TextStyle(color: Colors.white)),
), ),
TextButton.icon( TextButton.icon(
onPressed: () { onPressed: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (_) => builder: (_) => CallDetailsPage(history: history),
CallDetailsPage(history: history),
), ),
); );
}, },
icon: const Icon(Icons.info, color: Colors.white), icon: const Icon(Icons.info, color: Colors.white),
label: const Text('Details', label: const Text('Details', style: TextStyle(color: Colors.white)),
style: TextStyle(color: Colors.white)),
), ),
TextButton.icon( TextButton.icon(
onPressed: () async { onPressed: () async {
@ -498,29 +491,24 @@ class _HistoryPageState extends State<HistoryPage>
: null; : null;
if (phoneNumber == null) { if (phoneNumber == null) {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(content: Text('Contact has no phone number')),
content:
Text('Contact has no phone number')),
); );
return; return;
} }
if (isBlocked) { if (isBlocked) {
await BlockService().unblockNumber(phoneNumber); await BlockService().unblockNumber(phoneNumber);
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(content: Text('$phoneNumber unblocked')),
content: Text('$phoneNumber unblocked')),
); );
} else { } else {
await BlockService().blockNumber(phoneNumber); await BlockService().blockNumber(phoneNumber);
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
SnackBar( SnackBar(content: Text('$phoneNumber blocked')),
content: Text('$phoneNumber blocked')),
); );
} }
setState(() {}); setState(() {});
}, },
icon: Icon( icon: Icon(isBlocked ? Icons.lock_open : Icons.block,
isBlocked ? Icons.lock_open : Icons.block,
color: Colors.white), color: Colors.white),
label: Text(isBlocked ? 'Unblock' : 'Block', label: Text(isBlocked ? 'Unblock' : 'Block',
style: const TextStyle(color: Colors.white)), style: const TextStyle(color: Colors.white)),
@ -572,8 +560,7 @@ class CallDetailsPage extends StatelessWidget {
fallbackInitial: contact.displayName, fallbackInitial: contact.displayName,
) )
: CircleAvatar( : CircleAvatar(
backgroundColor: backgroundColor: generateColorFromName(contact.displayName),
generateColorFromName(contact.displayName),
radius: 30, radius: 30,
child: Text( child: Text(
contact.displayName.isNotEmpty contact.displayName.isNotEmpty
@ -613,8 +600,7 @@ class CallDetailsPage extends StatelessWidget {
if (contact.phones.isNotEmpty) if (contact.phones.isNotEmpty)
DetailRow( DetailRow(
label: 'Number:', label: 'Number:',
value: _obfuscateService value: _obfuscateService.obfuscateData(contact.phones.first.number),
.obfuscateData(contact.phones.first.number),
), ),
], ],
), ),

View File

@ -8,7 +8,6 @@ import 'package:flutter_contacts/flutter_contacts.dart';
import 'package:dialer/features/settings/settings.dart'; import 'package:dialer/features/settings/settings.dart';
import '../../services/contact_service.dart'; import '../../services/contact_service.dart';
import 'package:dialer/features/voicemail/voicemail_page.dart'; import 'package:dialer/features/voicemail/voicemail_page.dart';
import '../contacts/widgets/contact_modal.dart';
class _MyHomePageState extends State<MyHomePage> class _MyHomePageState extends State<MyHomePage>
@ -18,8 +17,6 @@ class _MyHomePageState extends State<MyHomePage>
List<Contact> _contactSuggestions = []; List<Contact> _contactSuggestions = [];
final ContactService _contactService = ContactService(); final ContactService _contactService = ContactService();
final ObfuscateService _obfuscateService = ObfuscateService(); final ObfuscateService _obfuscateService = ObfuscateService();
final TextEditingController _searchController = TextEditingController();
@override @override
void initState() { void initState() {
@ -35,15 +32,12 @@ class _MyHomePageState extends State<MyHomePage>
setState(() {}); setState(() {});
} }
void _clearSearch() {
_searchController.clear();
_onSearchChanged('');
}
void _onSearchChanged(String query) { void _onSearchChanged(String query) {
print("Search query: $query");
setState(() { setState(() {
if (query.isEmpty) { if (query.isEmpty) {
_contactSuggestions = List.from(_allContacts); // Reset suggestions _contactSuggestions = List.from(_allContacts);
} else { } else {
_contactSuggestions = _allContacts.where((contact) { _contactSuggestions = _allContacts.where((contact) {
return contact.displayName return contact.displayName
@ -56,7 +50,6 @@ class _MyHomePageState extends State<MyHomePage>
@override @override
void dispose() { void dispose() {
_searchController.dispose();
_tabController.removeListener(_handleTabIndex); _tabController.removeListener(_handleTabIndex);
_tabController.dispose(); _tabController.dispose();
super.dispose(); super.dispose();
@ -66,34 +59,6 @@ class _MyHomePageState extends State<MyHomePage>
setState(() {}); setState(() {});
} }
void _toggleFavorite(Contact contact) async {
try {
if (await FlutterContacts.requestPermission()) {
Contact? fullContact = await FlutterContacts.getContact(contact.id,
withProperties: true,
withAccounts: true,
withPhoto: true,
withThumbnail: true);
if (fullContact != null) {
fullContact.isStarred = !fullContact.isStarred;
await FlutterContacts.updateContact(fullContact);
setState(() {
// Updating the contact list after toggling the favorite
_fetchContacts();
});
}
} else {
print("Could not fetch contact details");
}
} catch (e) {
print("Error updating favorite status: $e");
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to update contact favorite status')),
);
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -115,53 +80,53 @@ class _MyHomePageState extends State<MyHomePage>
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color.fromARGB(255, 30, 30, 30), color: const Color.fromARGB(255, 30, 30, 30),
borderRadius: BorderRadius.circular(12.0), borderRadius: BorderRadius.circular(12.0),
border: Border.all(color: Colors.grey.shade800, width: 1), border: Border(
top: BorderSide(color: Colors.grey.shade800, width: 1),
left: BorderSide(color: Colors.grey.shade800, width: 1),
right: BorderSide(color: Colors.grey.shade800, width: 1),
bottom:
BorderSide(color: Colors.grey.shade800, width: 2),
),
), ),
child: SearchAnchor( child: SearchAnchor(
builder: builder:
(BuildContext context, SearchController controller) { (BuildContext context, SearchController controller) {
return GestureDetector( return SearchBar(
controller: controller,
padding:
WidgetStateProperty.all<EdgeInsetsGeometry>(
const EdgeInsets.only(
top: 6.0,
bottom: 6.0,
left: 16.0,
right: 16.0,
),
),
onTap: () { onTap: () {
controller.openView(); // Open the search view controller.openView();
_onSearchChanged('');
}, },
child: Container( backgroundColor: WidgetStateProperty.all(
decoration: BoxDecoration( const Color.fromARGB(255, 30, 30, 30)),
color: const Color.fromARGB(255, 30, 30, 30), hintText: 'Search contacts',
borderRadius: BorderRadius.circular(12.0), hintStyle: WidgetStateProperty.all(
border: Border.all( const TextStyle(color: Colors.grey, fontSize: 16.0),
color: Colors.grey.shade800, width: 1),
), ),
padding: const EdgeInsets.symmetric( leading: const Icon(
vertical: 12.0, horizontal: 16.0), Icons.search,
child: Row(
children: [
const Icon(Icons.search,
color: Colors.grey, size: 24.0),
const SizedBox(width: 8.0),
Text(
_searchController.text.isEmpty
? 'Search contacts'
: _searchController.text,
style: const TextStyle(
color: Colors.grey, fontSize: 16.0),
),
const Spacer(),
if (_searchController.text.isNotEmpty)
GestureDetector(
onTap: _clearSearch,
child: const Icon(
Icons.clear,
color: Colors.grey, color: Colors.grey,
size: 24.0, size: 24.0,
), ),
), shape:
], WidgetStateProperty.all<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
), ),
), ),
); );
}, },
viewOnChanged: (query) { viewOnChanged: (query) {
_onSearchChanged(query); // Update immediately _onSearchChanged(query);
}, },
suggestionsBuilder: suggestionsBuilder:
(BuildContext context, SearchController controller) { (BuildContext context, SearchController controller) {
@ -171,53 +136,7 @@ class _MyHomePageState extends State<MyHomePage>
title: Text(_obfuscateService.obfuscateData(contact.displayName), title: Text(_obfuscateService.obfuscateData(contact.displayName),
style: const TextStyle(color: Colors.white)), style: const TextStyle(color: Colors.white)),
onTap: () { onTap: () {
// Clear the search text input
controller.text = '';
// Close the search view
controller.closeView(contact.displayName); controller.closeView(contact.displayName);
// Show the ContactModal when a contact is tapped
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) {
return ContactModal(
contact: contact,
onEdit: () async {
if (await FlutterContacts
.requestPermission()) {
final updatedContact =
await FlutterContacts
.openExternalEdit(contact.id);
if (updatedContact != null) {
_fetchContacts();
Navigator.of(context).pop();
ScaffoldMessenger.of(context)
.showSnackBar(
SnackBar(
content: Text(
'${contact.displayName} updated successfully!'),
),
);
} else {
ScaffoldMessenger.of(context)
.showSnackBar(
SnackBar(
content: Text(
'Edit canceled or failed.'),
),
);
}
}
},
onToggleFavorite: () =>
_toggleFavorite(contact),
isFavorite: contact.isStarred,
);
},
);
}, },
); );
}).toList(); }).toList();

View File

@ -2,9 +2,4 @@
IMG=git.gmoker.com/icing/flutter:main IMG=git.gmoker.com/icing/flutter:main
if [ "$1" == '-s' ]; then docker run --rm -p 5037:5037 -v "$PWD:/app/" "$IMG" run
OPT+=(--dart-define=STEALTH=true)
fi
set -x
docker run --rm -p 5037:5037 -v "$PWD:/app/" "$IMG" run "${OPTS[@]}"

View File

@ -1,4 +1,3 @@
#!/usr/bin/env bash #!/usr/bin/env bash
echo "Running Icing Dialer in STEALTH mode..." echo "Running Icing Dialer in STEALTH mode..."
flutter run --dart-define=STEALTH=true flutter run --dart-define=STEALTH=true