Change colour of text in only one line of String Android - java

I'm currently developing an email application and when you go into the view screen it shows you the message at the top and if it is a series of emails, then underneath it will have:
------ Original Message ------
& the previous message below it.
Currently, all of this text is in white but I want to show all text in white apart from:
------ Original Message ------
As this is an email application and different providers format the original message string differently (see examples below:)
----Original Message----
---- Original Message ----
------Original Message------
------ Original Message ------
How would I go about having the whole line that contains 'Original Message' (and the dashes (however many dashes there are)) to be blue?
If this was a normal textView, and I knew the text that would be in the String, then I could use:
String startText = "Test - received<br><br>";
String textColor = "<font color='#0475F7'>-------- Original Message --------</font><br><br>";
String endText = "From: Test 124<br>" + "To: Test 125<br>" + "Subject: " + "<" + "confirm" + ">" + "<br>" + "Sent: January 30, 2017 3:40:42 PM GMT+00:00<br><br>" + "Test";
String text = startText + textColor + endText;
messageTextView.setText(Html.fromHtml(text), TextView.BufferType.SPANNABLE);
But as this is an email application, I would not be able to determine the text that will appear in the String every time, as each email is going to be different. The whole message (so top of the message, 'Original Message' line & the message beneath are all contained within one String).
I made an attempt with the below:
if (message.contains("Original Message")) {
int counter = message.split("-", -1).length - 1;
int sideone = counter / 2;
int sidetwo = counter / 2;
Log.d(TAG, "COUNTER: " + counter);
int startPosition = message.indexOf("-");
int endPosition = message.lastIndexOf("-");
Log.d(TAG, "START POS: " + startPosition + " " + "END POS: " + endPosition);
String requiredString = message.substring(startPosition, endPosition);
Log.d(TAG, "REQUIRED STRING: " + requiredString);
messageTextView.setText(message);
} else {
messageTextView.setText(message);
}
Whilst the counter would tell me how many dashes there are, I could not figure out how to use that to get the whole line with the dashes and the 'Original Message' part in the middle. Also using the requiredString method would work (but cut one dash off of the end) and if the user had used a dash somewhere in their email, then it wouldn't work as it would instead try to get the dashes before the 'Original Message' line.
Therefore, I'm looking for a way to do the following:
Retrieve the whole line in the String that contains the words 'Original Message' and all of the dashes that come with it
Set the colour to blue (#0475F7)
Set the textView with the text of the message at the top (in white) + the 'Original Message' line (in blue) followed by the message at the bottom (in white)

The best way to approach this is with HTML/XML tags. Android has classes that are able to parse HTML/XML and add the necessary styling needed. See this link.
If you don't want to go the HTML route, you can also check out the Span classes.
Hope this helps.

Managed to resolve this by using Spannable (as suggested above) and then checking the configuration.
if (message != null) {
if (message.contains("-------- Original Message --------")) {
Log.d(TAG, "Encromail configuration");
String wordToFind = "-------- Original Message --------";
Pattern word = Pattern.compile(wordToFind);
Matcher match = word.matcher(message);
Spannable wordToSpan = new SpannableString(message);
while (match.find()) {
Log.d(TAG, "Found Original Message string at index " + match.start() + " - " + (match.end() - 1));
wordToSpan.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.electric_blue)), match.start(), match.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
messageTextView.setText(wordToSpan);
} else if (message.contains("------ Original Message ------")) {
Log.d(TAG, "App to app configuration");
String wordToFind = "------ Original Message ------";
Pattern word = Pattern.compile(wordToFind);
Matcher match = word.matcher(message);
Spannable wordToSpan = new SpannableString(message);
while (match.find()) {
Log.d(TAG, "Found Original Message string at index " + match.start() + " - " + (match.end() - 1));
wordToSpan.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.electric_blue)), match.start(), match.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
messageTextView.setText(wordToSpan);
} else if (message.contains("------Original Message------")) {
Log.d(TAG, "BlackBerry 7 configuration");
String wordToFind = "------Original Message------";
Pattern word = Pattern.compile(wordToFind);
Matcher match = word.matcher(message);
Spannable wordToSpan = new SpannableString(message);
while (match.find()) {
Log.d(TAG, "Found Original Message string at index " + match.start() + " - " + (match.end() - 1));
wordToSpan.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.electric_blue)), match.start(), match.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
messageTextView.setText(wordToSpan);
} else {
Log.d(TAG, "Message doesn't contain Original Message string");
messageTextView.setText(message);
}
}

Related

Regex: starts with messages and string between parent message curly brace

I want to get all the message data. Such that it should look for message and all the data between curly braces of the parent message. With the below pattern, I am not getting all parent body.
String data = "syntax = \"proto3\";\r\n" +
"package grpc;\r\n" +
"\r\n" +
"import \"envoyproxy/protoc-gen-validate/validate/validate.proto\";\r\n" +
"import \"google/api/annotations.proto\";\r\n" +
"import \"google/protobuf/wrappers.proto\";\r\n" +
"import \"protoc-gen-swagger/options/annotations.proto\";\r\n" +
"\r\n" +
"message Acc {\r\n" +
" message AccErr {\r\n" +
" enum Enum {\r\n" +
" UNKNOWN = 0;\r\n" +
" CASH = 1;\r\n" +
" }\r\n" +
" }\r\n" +
" string account_id = 1;\r\n" +
" string name = 3;\r\n" +
" string account_type = 4;\r\n" +
"}\r\n" +
"\r\n" +
"message Name {\r\n" +
" string firstname = 1;\r\n" +
" string lastname = 2;\r\n" +
"}";
List<String> allMessages = new ArrayList<>();
Pattern pattern = Pattern.compile("message[^\\}]*\\}");
Matcher matcher = pattern.matcher(data);
while (matcher.find()) {
String str = matcher.group();
allMessages.add(str);
System.out.println(str);
}
}
I am expecting response like below in my array list of string with size 2.
allMessage.get(0) should be:
message Acc {
message AccErr {
enum Enum {
UNKNOWN = 0;
CASH = 1;
}
}
string account_id = 1;
string name = 3;
string account_type = 4;
}
and allMessage.get(1) should be:
message Name {
string firstname = 1;
string lastname = 2;
}
First remove the input prior to "message" appearing at the start of the line, then split on newlines followed by "message" (include the newlines in the split so newlines that intervene parent messages are consumed):
String[] messages = data.replaceAll("(?sm)\\A.*?(?=message)", "").split("\\R+(?=message)");
See live demo.
If you actually need a List<String>, pass that result to Arrays.asList():
List<String> = Arrays.asList(data.replaceAll("(?sm)\\A.*?(?=message)", "").split("\\R+(?=message)"));
The first regex matches everything from start up to, but not including, the first line that starts with message, which is replaced with a blank (ie deleted). Breaking the down:
(?sm) turns on flags s, which makes dot also match newlines, and m, which makes ^ and $ match start and end of each line
\\A means the very start of input
.*? .* means any quantity of any character (including newline as per the s flag being set), but adding ? makes this reluctant, so it matches as few characters as possible while still matching
(?=^message) is a look ahead and means the following characters are a start of a line then "message"
See regex101 live demo for a thorough explanation.
The split regex matches one or more line break sequences when they are followed by "message":
\\R+ means one or more line break sequences (all OS variants)
(?=message) is a look ahead and means the following characters are "message"
See regex101 live demo for a thorough explanation.
Try this for your regex. It anchors on message being the start of a line, and uses a positive lookahead to find the next message or the end of messages.
Pattern.compile("(?s)\r\n(message.*?)(?=(\r\n)+message|$)")
// or
Pattern.compile("(?s)\r?\n(message.*?)(?=(\r?\n)+message|$)")
No spliting, parsing, or managing nested braces either :)
https://regex101.com/r/Wa2xxx/1

How do I stop regex after finding "Message: "?

I'm splitting the body of a JSON message with the regex ":|\n" and storing the values into an array. I would like to get assistance with stopping my regex expression from splitting the message once it finds "Message: ".
In the JSON body, each section is separated by a new line, so the body looks similar to this:
{"body": "Name: Alfred Alonso\nCompany: null\nEmail: 123#abc.com\nPhone Number: 123-456-9999\nProject Type: Existing\nContact by: Email\nTime Frame: within 1 month\nMessage: Hello,\nThis is my message.\nThank You,\nJohn Doe"}
The code below works perfectly when the user doesn't create a new line within the message, so the entire message gets stored as one array value.
Thank you to anyone that can help me fix this!
String[] messArr = body.split(":|\n");
for (int i = 0; i < messArr.length; i++)
messArr[i] = messArr[i].trim();
if ("xxx".equals(eventSourceARN)) {
name = messArr[1];
String[] temp;
String delimiter = " ";
temp = name.split(delimiter);
name = temp[0];
String lastName = temp[1];
company = messArr[3];
email = messArr[5];
phoneNumber = messArr[7];
projectType = messArr[9];
contactBy = messArr[11];
timeFrame = messArr[13];
message = messArr[15];
I would like
messArr[14] = "Message"
messArr[15] = "Hello, This is my message. Thank you, John Doe"
This is what I get
[..., Message, Hello,, This is my message., Thank You, John Doe].
messArr[14] = "Message"
messArr[15] = "Hello,"
messArr[16] = "This is my message."
messArr[17] = "Thank You,"
messArr[18] = "John Doe"
Instead of using split, you can use a find loop, e.g.
Pattern p = Pattern.compile("([^:\\v]+): |((?<=Message: )(?s:.*)|(?<!$).*)\\R?");
List<String> result = new ArrayList<>();
for (Matcher m = p.matcher(input); m.find(); )
result.add(m.start(1) != -1 ? m.group(1) : m.group(2));
Test
String input = "Name: Alfred Alonso\n" +
"Company: null\n" +
"Email: 123#abc.com\n" +
"Phone Number: 123-456-9999\n" +
"Project Type: Existing\n" +
"Contact by: Email\n" +
"Time Frame: within 1 month\n" +
"Message: Hello,\n" +
"This is my message.\n" +
"Thank You,\n" +
"John Doe";
Pattern p = Pattern.compile("([^:\\v]+): |((?<=Message: )(?s:.*)|(?!$).*)\\R?");
List<String> result = new ArrayList<>();
for (Matcher m = p.matcher(input); m.find(); )
result.add(m.start(1) != -1 ? m.group(1) : m.group(2));
for (int i = 0; i < result.size(); i++)
System.out.println("result[" + i + "]: " + result.get(i));
Output
result[0]: Name
result[1]: Alfred Alonso
result[2]: Company
result[3]: null
result[4]: Email
result[5]: 123#abc.com
result[6]: Phone Number
result[7]: 123-456-9999
result[8]: Project Type
result[9]: Existing
result[10]: Contact by
result[11]: Email
result[12]: Time Frame
result[13]: within 1 month
result[14]: Message
result[15]: Hello,
This is my message.
Thank You,
John Doe
Explanation
Match one of:
( Start capture #1
[^:\v]+ Match one or more characters that are not a : or a linebreak
) End capture #1
: Match, but don't capture, a : and a space (which SO is hiding here)
| or:
( Start capture #2
Match one of:
(?<=Message: )(?s:.*) Rest of input, i.e. all text including linebreaks, if the text is immediately preceded by "Message: "
| or:
(?!$) Don't match if we're already at end-of-input
.* Match 0 or more characters up to end-of-line, excluding the EOL
) End capture #2
\\R? Match, but don't capture, an optional linebreak. This doesn't apply to Message text, and is optional in case there is no Message text and no linebreak after last value
If you want to, you could do exactly what you are doing and then put things together later. As you are trimming, notice where it says Message, then know that the Message is in the next slot and beyond. Then put it back together.
int messagePosition = -1;
for (int i = 0; i < messArr.length; i++){
messArr[i] = messArr[i].trim();
if (i>0 && messArr[i-1].equals("Message")){
messagePosition =i;
}
}
if (messagePosition > -1){
for (int i=messagePosition+1; i <messArr.length; i++){
messArr[messagePosition]=messArr[messagePosition]+" "+messArr[i];
}
}
One downside is that because arrays are fixed size, you need to act as if there is nothing beyond the messagePosition. So any calculations with length will be misleading. If for some reason you are worried you will look in the slots beyond, you could add messArr[i]=""; to the second for loop after the concatenation step.

API endpoint how to return a string with new line

In my custom GET endpoint, I would like to return a String but, to "beautify" it, I would like to insert new line for each different information. I've tried with \n\r this way
return "Name of process definition: "+ obj.getString("processDefinitionName") + "\r\n" + "Start time of instance: " + obj.getString("startTime")
+ "||" + "End time of the instance: " + obj.getString("endTime") + "||" + "Total duration time(ms): " + obj.getInt("durationInMillis");
but it only prints a white space and the text is written on the same line. Why is it not working?
You have to use HTML tags to display new line on browser. You can use <br/> tag to add new line. Browser parses your response in HTML form.

How to remove specific line from multi-line string

I have below java string as command output
String output = "NIC Value\n"
+ "------ -----\n"
+ "vmn0 on \n"
+ "vmn1 on \n"
+ "vmn2 on \n"
+ "vmn3 on \n"
+ "vmn4 on";
I want to remove second line with dash from above string. How can I do it?
I tried it using contains method but it is generating blank line after removing second line.
if(output!=null && output.contains("-"))
output = output.replace("-","");
This is complete answer you are looking for:
String output = "NIC Value\n"
+ "------ -----\n"
+ "vmn0 on \n"
+ "vmn1 on \n"
+ "vmn2 on \n"
+ "vmn3 on \n"
+ "vmn4 on";
String str = Stream.of(output.split("\n"))
.filter(s -> !s.contains("--"))
.collect(Collectors.joining("\n"));
You can use this to remove that line and use the result,
String result = output.replace("------ -----\n", "");
It will replace that line with an empty String

How to add line breaks to prompt text in JavaFX?

I have a TextArea that has some prompt text that I want to be split onto a few different lines, however, line breaks don't work in prompt text for some reason.
Code:
TextArea paragraph = new TextArea();
paragraph.setWrapText(true);
paragraph.setPromptText(
"Stuff done today:\n"
+ "\n"
+ "- Went to the grocery store\n"
+ "- Ate some cookies\n"
+ "- Watched a tv show"
);
Result:
As you can see, the text does not line break properly. Does anyone know how to fix this?
The prompt is internally shown by a node of type Text which can handle line breaks. So the interesting question is why don't they show? Reason is revealed by looking at the promptText property: it is silently replacing all \n with empty strings:
private StringProperty promptText = new SimpleStringProperty(this, "promptText", "") {
#Override protected void invalidated() {
// Strip out newlines
String txt = get();
if (txt != null && txt.contains("\n")) {
txt = txt.replace("\n", "");
set(txt);
}
}
};
A way around (not sure if it is working on all platforms - does on my win) is to use the \r instead:
paragraph.setPromptText(
"Stuff done today:\r"
+ "\r"
+ "- Went to the grocery store\r"
+ "- Ate some cookies\r"
+ "- Watched a tv show"
);

Categories