Skip to content

Commit

Permalink
Merge pull request #124 from nucleus-ffm/telephone-number
Browse files Browse the repository at this point in the history
Telephone number
  • Loading branch information
nucleus-ffm committed Jun 30, 2024
2 parents 0badce2 + c90a902 commit 720be66
Show file tree
Hide file tree
Showing 6 changed files with 328 additions and 91 deletions.
4 changes: 2 additions & 2 deletions lib/services/sortWarnings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import '../main.dart';

void sortWarnings(List<WarnMessage> list) {
if (userPreferences.sortWarningsBy == "severity") {
list.sort((a, b) => Severity.getIndexFromSeverity(b.severity)
.compareTo(Severity.getIndexFromSeverity(a.severity)));
list.sort((a, b) => Severity.getIndexFromSeverity(a.severity)
.compareTo(Severity.getIndexFromSeverity(b.severity)));
} else if (userPreferences.sortWarningsBy == "date") {
list.sort((a, b) => b.sent.compareTo(a.sent));
} else if (userPreferences.sortWarningsBy == "source") {
Expand Down
59 changes: 51 additions & 8 deletions lib/services/urlLauncher.dart
Original file line number Diff line number Diff line change
@@ -1,37 +1,80 @@
import "package:url_launcher/url_launcher.dart";

String? extractWebAddress(String text) {
Uri? extractWebAddress(String text) {
if (text.startsWith("<a")) {
// extract address from HTML-formatted tag
int beginIndex = text.indexOf("href=\"") + 6;
int endIndex = text.indexOf("\"", beginIndex);
text = text.substring(beginIndex, endIndex);
}

// if the url is an email address, try adding a mailto and launch this
final RegExp emailAddressRegEx = RegExp(
r"""(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])""");
if (emailAddressRegEx.firstMatch(text) != null) {
if (!text.startsWith("mailto:")) {
text = "mailto:" + text;
}

return Uri.parse(text);
}

// if the url does not have a protocol, we use http
// e.g. www.example.de -> http://www.example.de, example.de -> http://example.de
if (!(text.startsWith("http") || text.startsWith("https"))) {
text = "http://" + text;
}

final RegExp webAddressRegEx = RegExp(
r"((http|https)://)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)");

final RegExpMatch? match = webAddressRegEx.firstMatch(text);
if (match != null && match.start == 0 && match.end == text.length) {
return text;
return Uri.parse(text);
}

return null;
}

Future<void> launchUrlInBrowser(String url) async {
String? webAddress = extractWebAddress(url);
Future<bool> launchUrlInBrowser(String url) async {
Uri? webUri = extractWebAddress(url);

if (webAddress == null) {
return;
if (webUri == null) {
return false;
}

Uri webUri = Uri.parse(webAddress);
if (await canLaunchUrl(webUri)) {
await launchUrl(webUri, mode: LaunchMode.externalApplication);
} else {
throw "Could not launch ${webUri.toString()}";
print("Could not launch ${webUri.toString()}");
return false;
}

return true;
}

List<String?> extractAllPhoneNumbers(String text) {
// remove some chars to detect even weird formatted phone numbers
RegExp expToRemove = RegExp(r"[\s/-]");
text = text.replaceAll(expToRemove, "");

// @todo this regex can certainly be further improved
/*
* (+\d{1,3}\s?)? - This part recognizes an optional country code starting with a plus sign (+) followed by 1 to 3 digits and an optional space.
* ((\d{1,3})\s?)? - This part recognizes an optional prefix in parentheses, starting with an opening parenthesis "(", followed by 1 to 3 digits, a closing parenthesis ")" and an optional space.
* \d{1,4} - This part recognizes 1 to 4 digits for the main number.
* [\s.-]? - This part recognizes an optional space, a hyphen "-" or a period "." as a separator.
* \d{1,4} - This part recognizes 1 to 4 digits for the second number group.
* [\s.-]? - This part again recognizes an optional space, a hyphen "-" or a period "." as a separator.
* \d{1,9} - This part recognizes 1 to 9 digits for the third number group.
*/
RegExp phoneNumberRegex = RegExp(
r"(\+\d{1,3}\s?)?(\(\d{1,3}\)\s?)?\d{1,4}[\s.-]?\d{1,4}[\s.-]?\d{1,9}");

return phoneNumberRegex
.allMatches(text)
.map((regExpMatch) => regExpMatch.group(0))
.toList();
}

String? extractPhoneNumber(String text) {
Expand Down
128 changes: 82 additions & 46 deletions lib/views/WarningDetailView.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,58 @@ class _DetailScreenState extends State<DetailScreen> {
return replacedText;
}

String generateURL(String url) {
String correctURL = "";
if (url.startsWith('http')) {
correctURL = url;
} else if (url.startsWith("<a")) {
int beginURL = url.indexOf("\"") + 1;
int endURL = url.indexOf("\"", beginURL + 1);
correctURL = url.substring(beginURL, endURL);
} else {
int firstPoint = url.indexOf('.');
String domain = url.substring(firstPoint + 1, url.length);
correctURL = 'https://' + domain;
/// generate a TextSpan with tappable telephone numbers
List<TextSpan> generateContactBody(String text) {
List<TextSpan> result = [];
List<String?> allPhoneNumbers = extractAllPhoneNumbers(text);

if (allPhoneNumbers.length == 0) {
result.add(TextSpan(text: text));
return result;
}

int pointer = 0;
for (String? phoneNumber in allPhoneNumbers) {
if (phoneNumber == null) {
continue;
}

// find the start position of the telephone number in the text
int startPos = text.indexOf(phoneNumber.substring(0, 2), pointer);

//To find the end position of the telephone number in the text, we use
// the last 2 digits and search from the current pointer + the length of
// the telephone -3 to find the last 2 digits. We have to do it that way
// because the telephone numbers in the text can contain spaces. The
// extracted telephone numbers we have in allPhoneNumbers
// don't have spaces anymore
int endPos = text.indexOf(
phoneNumber.substring(phoneNumber.length - 2, phoneNumber.length),
pointer + phoneNumber.length - 3) +
2;

if (startPos != -1 && endPos != -1) {
// add the text before the telephone number to a TextSpan
result.add(TextSpan(text: text.substring(pointer, startPos)));
// add the clickable telephone number
result.add(TextSpan(
text: phoneNumber,
style: TextStyle(color: Theme.of(context).colorScheme.tertiary),
recognizer: TapGestureRecognizer()
..onTap = () {
// print("phone number tapped");
makePhoneCall(phoneNumber);
}));
pointer = endPos;
}
}

// add remaining text after the last telephone number
if (pointer < text.length) {
result.add(TextSpan(text: text.substring(pointer, text.length)));
}
print("correct URL: " + correctURL);
return correctURL;

return result;
}

/// returns the given text as List of TextSpans with clickable links and
Expand Down Expand Up @@ -137,11 +174,12 @@ class _DetailScreenState extends State<DetailScreen> {
print("startPos $startPos");
if (startPos == -1) {
returnList.add(TextSpan(
text: text.substring(pointer, text.length),
recognizer: TapGestureRecognizer()
text: text.substring(pointer, text.length),
/*recognizer: TapGestureRecognizer()
..onTap = () {
print("text tapped");
}));
}*/
));
pointer = text.length;
} else {
print("pointer: $pointer startPos: $startPos");
Expand Down Expand Up @@ -855,22 +893,42 @@ class _DetailScreenState extends State<DetailScreen> {
widget._warnMessage.contact != ""
? Row(
children: [
Icon(Icons.call),
Icon(Icons.perm_contact_cal),
SizedBox(
width: 15,
),
Flexible(
child: SelectableText.rich(
// key used by unit test
key: Key('contactFieldKey'),
TextSpan(
children: generateContactBody(replaceHTMLTags(
widget._warnMessage.contact)),
style: TextStyle(
fontSize: userPreferences.warningFontSize)),
),
),
],
)
: SizedBox(),
widget._warnMessage.web != ""
? Row(
children: [
Icon(Icons.open_in_new),
SizedBox(
width: 5,
),
Flexible(
fit: FlexFit.loose,
child: TextButton(
onPressed: () async {
bool success = await makePhoneCall(
widget._warnMessage.contact);
// display error message in snackBar if
// launch was not successful
bool success = await launchUrlInBrowser(
widget._warnMessage.web);

if (!success) {
final snackBar = SnackBar(
content: const Text(
'Keine Telefonnummer gefunden',
'Kann URL nicht öffnen',
//@todo translate
style: TextStyle(color: Colors.black),
),
Expand All @@ -883,29 +941,7 @@ class _DetailScreenState extends State<DetailScreen> {
}
},
child: Text(
replaceHTMLTags(widget._warnMessage.contact),
style: TextStyle(
fontSize: userPreferences.warningFontSize),
),
),
)
],
)
: SizedBox(),
widget._warnMessage.web != ""
? Row(
children: [
Icon(Icons.open_in_new),
SizedBox(
width: 5,
),
Flexible(
fit: FlexFit.loose,
child: TextButton(
onPressed: () =>
launchUrlInBrowser(widget._warnMessage.web),
child: Text(
generateURL(widget._warnMessage.web),
widget._warnMessage.web,
style: TextStyle(
fontSize: userPreferences.warningFontSize),
),
Expand Down
5 changes: 5 additions & 0 deletions test/extractPhoneNumber_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,10 @@ void main() {

expect(extractPhoneNumber(text), null);
});
test('test extractPhoneNUmber with 110', () {
String text = "call 110";

expect(extractPhoneNumber(text), "110");
});

}
Loading

0 comments on commit 720be66

Please sign in to comment.