Could you please help me find a solution for my code? I'm making a new Android app in which I need to make some calculations and the scenario is the following:
There are four fields to be calculated. Two EditText (number decimal) field are obligatory and the other two are optional, BUT, if the optional fields are filled, then it needs to be in the calculation, otherwise only the obligatory fields will be used.
Right now I'm totally OK with calculating the obligatory fields but when I try some if-else clause to include the optional fields in the calculation, the app goes bananas.
I'm not sure where I should make this two-step option, if I should use boolean to check the option field condition, if I just keep using if-else...
The problem is not the calculatin itself, but having two ways for the code to follow: One using only the obligatory fields if nothing else is inserted and the other one using all four fields.
Thanks everyone!
Code below is only using the two obligatory fields.
public void calcularResultado(View view) {
//check for blank values in obligatory fields
if (editGasolina.length() == 0) {
editGasolina.setError("Insira o valor");
}
if (editEtanol.length() == 0) {
editEtanol.setError("Insira o valor");
//runs the code
} else {
double valorGasolina = Double.parseDouble(editGasolina.getText().toString());
double valorEtanol = Double.parseDouble(editEtanol.getText().toString());
double valorResultado = valorEtanol / valorGasolina;
double porcentagem = (valorResultado) * 100;
String valorResultadoTexto = Double.toString(porcentagem);
valorResultadoTexto = String.format("%.2f", porcentagem);
if (valorResultado >= 0.7) {
textResultado.setText("GASOLINA");
textRendimento.setText(valorResultadoTexto + "%");
} else {
textResultado.setText("ETANOL");
textRendimento.setText(valorResultadoTexto + "%");
}
You almost got it. What happens now, since you have an if-if-elseconstruction, it considers the first if statement to be seperate from the if-else block below. That is to say, if editEtanol.length() == 0 evaluates to false, it will execute the else block below, even if editGasolina.length() == 0 evaluates to true.
Changing the line if (editEtanol.length() == 0) { to else if (editEtanol.length() == 0) { should already help alot. Hope that helps!
public void calcularResultado(View view) {
//check for blank values in obligatory fields
if (editGasolina.length() == 0) {
editGasolina.setError("Insira o valor");
}
if (editEtanol.length() == 0) {
editEtanol.setError("Insira o valor");
//runs the code
} else {
double valorGasolina = Double.parseDouble(editGasolina.getText().toString());
double valorEtanol = Double.parseDouble(editEtanol.getText().toString());
boolean optionalField1Used = optionalEditText1.length() != 0;
boolean optionalField2Used = optionalEditText2.length() != 0;
double valorResultado = 0;
if (!optionalField1Used && !optionalField2Used) {
valorResultado = valorEtanol / valorGasolina;
} else if (optionalField1Used && !optionalField2Used) {
valorResultado = //some other calculation
} else if (!optionalField1Used && optionalField2Used) {
valorResultado = //yet another calculation
} else {
valorResultado = //calculation if both optional fields used
}
double porcentagem = (valorResultado) * 100;
String valorResultadoTexto = Double.toString(porcentagem);
valorResultadoTexto = String.format("%.2f", porcentagem);
if (valorResultado >= 0.7) {
textResultado.setText("GASOLINA");
textRendimento.setText(valorResultadoTexto + "%");
} else {
textResultado.setText("ETANOL");
textRendimento.setText(valorResultadoTexto + "%");
}
Let us assume that the optional fields are called edit1 and edit2. I also assume that in order to use the alternative computation, both optional values must be present.
To enhance code clarity, I would define two Boolean variables to explicitly indicate whether the mandatory and optional fields have values. Something like the following.
public void calcularResultado(View view) {
var mandatoryValues = true;
var optionalValues = false;
if (editGasolina.length() == 0 {
editGasolina.setError("Insira o valor");
mandatoryValues = false;
}
if (editEtanol.length() == 0 {
editEtanol.setError("Insira o valor");
mandatoryValues = false;
}
if (edit1.length() > 0 && edit2.length() > 0) {
optionalValues = true;
}
if (mandatoryValues) {
if (optionalValues) {
// do alternative computation
} else {
// do computation for mandatory values only
}
}
}
Note that if either mandatory value is absent, no computation is performed.
Hope it helps - Carlos
Related
Is there a better way to write this constructor which has multiple if statements and multiple arguments? I'm a noob to programming so any leads would be helpful.
public Latency(final double full, final double cpuOne, final double cpuTwo, final double cpuThree, final double cpuFour) {
if (full > 10.0 || (full <= 0.0)) {
throw new IllegalArgumentException("Must check the values");
}
this.full = full;
if (cpuOne == 0 && cpuTwo == 0 && cpuThree == 0 && cpuFour == 0) {
throw new IllegalArgumentException("not all can be zero");
} else {
if (cpuOne == 0.5) {
this.cpuOne = full;
} else {
this.cpuOne = cpuOne;
}
if (cpuTwo == 0.5) {
this.cpuTwo = full;
} else {
this.cpuTwo = cpuTwo;
}
if (cpuThree == 0.5) {
this.cpuThree = full;
} else {
this.cpuThree = cpuThree;
}
if (cpuFour == 0.5) {
this.cpuFour = full;
} else {
this.cpuFour = cpuFour;
}
}
}
I think this code doesn't need much of context as it is pretty straight forward.
I found out that we can't use switch statements for type double. How to optimize this?
There are a number of possible ways of refactoring the code that you've written, and there are pros and cons of each one. Here are some ideas.
Idea One - use the conditional operator
You could replace the else block with code that looks like this. This is just effectively a shorter way of writing each of the inner if/else blocks. Many people find this kind of form more readable than a bunch of verbose if/else blocks, but it takes some time to get used to it.
this.cpuOne = cpuOne == 0.5 ? full : cpuOne;
this.cpuTwo = cpuTwo == 0.5 ? full : cpuTwo;
this.cpuThree = cpuThree == 0.5 ? full : cpuThree;
this.cpuFour = cpuFour == 0.5 ? full : cpuFour;
Idea Two - move common functionality to its own method
You could have a method something like this
private static double changeHalfToFull(double value, double full) {
if (value == 0.5) {
return full;
} else {
return value;
}
}
then call it within your constructor, something like this.
this.cpuOne = changeHalfToFull(cpuOne);
this.cpuTwo = changeHalfToFull(cpuTwo);
this.cpuThree = changeHalfToFull(cpuThree);
this.cpuFour = changeHalfToFull(cpuFour);
This has the advantage that the key logic is expressed only once, so it's less error prone than repeating code over and over.
Idea Three - use arrays
You could use an array of four elements in the field that stores these values. You could also use an array for the constructor parameter. This has a huge advantage - it indicates that the four CPU values are somehow all "the same". In other words, there's nothing special about cpuOne compared to cpuTwo, for example. That kind of messaging within your code has real value to someone trying to understand this.
public Latency(final double full, final double[] cpuValues) {
// validation conditions go here ...
this.cpuValues = new double[4];
for (int index = 0; index <= 3; index++) {
if (cpuValues[index] == 0.5) {
this.cpuValues[index] = full;
} else {
this.cpuValues[index] = cpuValues[index];
}
}
}
Or a combination
You could use some combination of all these ideas. For example, you might have something like this, which combines all three of the above ideas.
public Latency(final double full, final double[] cpuValues) {
// validation conditions go here ...
this.cpuValues = new double[4];
for (int index = 0; index <= 3; index++) {
this.cpuValues[index] = changeHalfToFull(cpuValues[index]);
}
}
private static double changeHalfToFull(double value, double full) {
return value == 0.5 ? full : value;
}
There are obviously other possibilities. There is no single correct answer to this question. You need to choose what you're comfortable with, and what makes sense in the larger context of your project.
DRY - Don't Repeat Yourself
Each if is essentially the same. Put it in a separate method and call the method once for each cpu* variable.
public class Latency {
private double full;
private double cpuOne;
private double cpuTwo;
private double cpuThree;
private double cpuFour;
public Latency(final double full,
final double cpuOne,
final double cpuTwo,
final double cpuThree,
final double cpuFour) {
if (full > 10.0 || (full <= 0.0)) {
throw new IllegalArgumentException("Must check the values");
}
this.full = full;
if (cpuOne == 0 && cpuTwo == 0 && cpuThree == 0 && cpuFour == 0) {
throw new IllegalArgumentException("not all can be zero");
}
else {
this.cpuOne = initCpu(cpuOne);
this.cpuTwo = initCpu(cpuTwo);
this.cpuThree = initCpu(cpuThree);
this.cpuFour = initCpu(cpuFour);
}
}
private double initCpu(double cpu) {
return cpu == 0.5 ? full : cpu;
}
public static void main(String[] arg) {
new Latency(9.99, 8.0, 7.0, 6.0, 0.5);
}
}
I'm trying to program a bug to move around an array attached to a custom Room object, whilst keeping count of how many times each tile has been stepped on.
The Room object is working properly, as are the movement and the counting. However, the bug's coordinates, bugX and bugY, are somehow reverting to 0 after exiting the nextMove method. Their values only revert when exiting the method; even the last line of code in the nextMove method itself uses their new values.
Relevant portion of the method is attached, but other sections can be added upon request.
if (dirNum == 0 && bugY < length-1) //Move up
bugY++;
else if (dirNum == 1 && bugX < width-1) //Move right
bugX++;
else if (dirNum == 2 && bugY > 0) //Move down
bugY--;
else if (dirNum == 3 && bugX > 0) //Move left
bugX--;
else {
System.out.println("Error: Cannot move " + direction + ".");
canMove = false;
dirNum = generator.nextInt(4);
continue;
}
This is the context for the command itself.
while (endSim == false) {
nextMove(bugX, bugY);
System.out.print(room.printRoom() + "\n\nNext move? (y/n) ");
simSentinel = in.next();
if (simSentinel.charAt(0) == 'n')
endSim = true;
}
The declarations where the starting coordinates are assigned aren't inside any loops, let alone where the variable itself is called.
The problem is the one described by #T.J.Crowder in his answer though applied to java.
Variables passed as parameters in java are passed by value. If the value is changed by the method receiving the parameter, the change only affects the value inside that method. The "outside" value doesn't change.
What you can do is to encapsulate the coords in an object and pass the encapsulating object as a parameter.
Then the method will receive the object by value, and change it's state (instead of the value of the object).
For a deeper understanding see this question
EDIT I:
I cleand up the code a bit. Though it is is missing the declaration of room and simSentinel, if you add that you should have a running example.
public class Bug{
public int x=0;
public int y=0;
}
public class SimpleSim {
private int dirNum = 0;
private int length = 20;
private int width = 20;
private boolean canMove = true;
private Random generator = new Random();
private boolean endSim = false;
public static void main(String [] args) {
SimpleSim simpleSim = new SimpleSim();
simpleSim.start();
}
private void start() {
Bug myBug = new Bug();
// Give the bug some initial x, y values.
myBug.x = 0;
myBug.y = 0;
while (endSim == false) {
nextMove(myBug);
System.out.print(room.printRoom() + "\n\nNext move? (y/n) ");
simSentinel = in.next();
if (simSentinel.charAt(0) == 'n')
endSim = true;
}
}
}
public void nextMove(Bug bug){
if (dirNum == 0 && bug.y < length-1) //Move up
bug.y++;
else if (dirNum == 1 && bug.x < width-1) //Move right
bug.x++;
else if (dirNum == 2 && bug.y > 0) //Move down
bug.y--;
else if (dirNum == 3 && bug.x > 0) //Move left
bug.x--;
else {
System.out.println("Error: Cannot move " + "?" + ".");
canMove = false;
dirNum = generator.nextInt(4);
}
}
}
It seems that you are passing your bugX and bugY parameters by value. In this case, changing their value inside the method won't affect their values outside the method.
You may want to make your nextMove method return the new values for bugX and bugY after they are computed so that you can gather them back into your actual bugX and bugY variables
Im developing a small app and I wants to get the total when I click the button total . But if there are null values the code code dosen't work .So additionally following code was added .
int QtyOfChickenBurger;
if ((textField.getText().equals(null))) {
QtyOfChickenBurger = Integer.parseInt(textField.getText())*0;
} else {
QtyOfChickenBurger = Integer.parseInt(textField.getText()) * 70;
}
But still my application don't output the total when the textField is empty. So please help me to fix this.This is the full code.
JButton bttotal = new JButton("Total");
bttotal.setBounds(21, 37, 145, 25);
bttotal.setFont(new Font("Thoma", Font.PLAIN, 12));
bttotal.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg) {
int QtyOfChickenBurger;
if ((textField.getText().equals(null))) {
QtyOfChickenBurger = Integer.parseInt(textField.getText())*0;
} else {
QtyOfChickenBurger = Integer.parseInt(textField.getText()) * 70;
}
int QtyOfChickenBurgerMeal = Integer.parseInt(textField_2.getText()) * 120;
int QtyOfCheeseBurger = Integer.parseInt(textField_3.getText()) * 340;
int QtyOfDrinks = Integer.parseInt(enterQTY.getText());
int spriteCost = Integer.parseInt(enterQTY.getText()) * 55;
int cokaColaCost = Integer.parseInt(enterQTY.getText()) * 60;
int pepsiCost = Integer.parseInt(enterQTY.getText()) * 40;
int lemonJuceCost = Integer.parseInt(enterQTY.getText()) * 35;
int sum = (QtyOfChickenBurger + QtyOfChickenBurgerMeal + QtyOfCheeseBurger);
lblDisplayCostOfMeals.setText(Integer.toString(sum));
String x = String.valueOf(comboBox.getSelectedItem());
if (x == "Sprite") {
lblDisplayCostOfDrinks.setText(Integer.toString(spriteCost));
} else if (x == "Select the drink") {
lblDisplayCostOfDrinks.setText("0");
} else if (x == "Coka Cola") {
lblDisplayCostOfDrinks.setText(Integer.toString(cokaColaCost));
} else if (x == "Pepsi") {
lblDisplayCostOfDrinks.setText(Integer.toString(pepsiCost));
} else if (x == "Lemon juce") {
lblDisplayCostOfDrinks.setText(Integer.toString(lemonJuceCost));
}
}
});
This code is wrong
if ((textField.getText().equals(null))) {
QtyOfChickenBurger = Integer.parseInt(textField.getText())*0;
}
if the textField.getText() is null then you can not parse the null into Integer. In addition, you multiply 0 with a number for what? => just set it to 0.
Change it to
if (textField.getText()==null || textField.getText().equals("")){
QtyOfChickenBurger = 0;
}
I presume you are using Swing JTextField which is a JTextComponent.
getText is returning a String therefore, you will need to check if its null or empty, before parsing it to int later on.
There are few ways of checking it, !textField.getText().isEmpty() and textField.getText() != null
Personally, i would use Commons Lang library StringUtils and just check if the string isBlank(textField.getText())
Additionally you should validate the input, as well You can use the mentioned library and use StringUtils.isNumeric() in your if statement.
First of all textField.getText().equals(null) will never work this only throws a NullPointerException better use textField.getText()==null.
Because the user can enter anything in the TextField you need to validate the input or create a try-catch-block.
The best way would to create a help method to parse the numbers, for example:
public static int readInteger(String text, int defaultValue) {
if(text == null)
return defaultValue;
try {
return Integer.parseInt(text);
} catch (NumberFormatException nfe) {
return defaultValue;
}
}
By the way don't compare Strings with x == "Pepsi" only when you want to check if the String is null.
Read: How do I compare strings in Java?
I am assuming that user can write what he want in that textfield so u should also take care when textfield have a value like 'asd';
String QtyOfChickenBurgerAsString =textField.getText();
Integer QtyOfChickenBurger=null;
try{
QtyOfChickenBurger = Integer.valueOf(QtyOfChickenBurgerAsString);
}catch(Exception ex){
System.out.println(" this is not a number ...do whatever u wanna do");
}
if (QtyOfChickenBurger!=null){
System.out.println("integer value of field text "+QtyOfChickenBurger);
}
I suggest create variables with starting letter in lower case ,and when you compare using equals use the constant first.Also you could find a better solution by using components which accept only numbers in their text.
As of java 9, the Object class has a convenience method for handling null values and it is found in java.util.
public static String processEmployee(String fullName){
// if fullName is null then second argument is returned,otherwise fullName is
String name = Objects.requireNonNullElse(fullName, "empty");
// the processing goes here ...
return name;
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I'm making a terrible 2D remake of Minecraft as a class project in java, and I have a crafting bench thing (or whatever it's called) and I have an if statement that checks if you have one piece of wood in the top left and nothing in the other 3, or if you have one piece of wood in the top right and nothing in the other 3, and so on...
The following if statement which I'm using seems to let you have a "wood" block in multiple slots at the same time and still lets you get the "plank" block. (id1 - id4 represent the crafting bench slots) 2x2 crafting bench Tile.wood is a wood block, Tile.blank is basically a null block or nothing.
//this if statement is what I need to change maybe?
if ((id1.id == Tile.wood && id2.id == Tile.blank
&& id3.id == Tile.blank && id4.id == Tile.blank) ||
(id1.id == Tile.blank && id2.id == Tile.wood
&& id3.id == Tile.blank && id4.id == Tile.blank) ||
(id1.id == Tile.blank && id2.id == Tile.blank
&& id3.id == Tile.wood && id4.id == Tile.blank) ||
(id1.id == Tile.blank && id2.id == Tile.blank
&& id3.id == Tile.blank && id4.id == Tile.wood)) {
//this code I don't need to change, it works fine
Inventory.inv_result.id = Tile.plank;
Inventory.inv_result.blockNum += 4;
System.out.println("You can have 4 planks");
}
So once again you should only be allowed to have one block in one place at one time, otherwise it will do nothing.
How can I fix it so I only get the "plank" block if there is only a single "wood" block in one of the four slots?
The if-condition looks untidy but will work perfectly fine. However, you can modularize it for better understanding & debugging.
Create few utility methods to do jobs for you, as below:
boolean isWood(<id object>) {
if(Tile.wood.equals(<id object>))
return true;
else
return false;
}
boolean isBlank(<id object>) {
if(Tile.blank.equals(<id object>))
return true;
else
return false;
}
void doProcess(){
Inventory.inv_result.id = Tile.plank;
Inventory.inv_result.blockNum += 4;
System.out.println("You can have 4 planks");
}
Then re-write your if-condition like below
if(isWood(id1.id) && isBlank(id2.id) && isBlank(id3.id) && isBlank(id4.id))
doProcess();
else if(isWood(id2.id) && isBlank(id1.id) && isBlank(id3.id) && isBlank(id4.id))
doProcess();
else if(isWood(id3.id) && isBlank(id2.id) && isBlank(id1.id) && isBlank(id4.id))
doProcess();
else if(isWood(id4.id) && isBlank(id2.id) && isBlank(id3.id) && isBlank(id1.id))
doProcess();
Shishir
i think if statement is ok even though it is so long. Try to use .equal() instead of == in the if statement.
The problem you have with your if-condition has nothing to do with the if-condition but some other part of your crafting bench code. You should post your crafting bench code for us to find the actual problem.
Besides about the untidiness and stuff...
This may not be the answer to your question but this will definitely make it easier for you to do many recipes:
// These are your function calls. One line per recipe. Way better than multiple if functions
// example for shaped crafting using minecraft's crafting bench recipe.
shapedCrafting(new int[]{Tile.plank, Tile.plank, Tile.plank, Tile.plank}, 4, Tile.plank);
// example for shapeless crafting using your recipe
shapelessCrafting(new int[]{Tile.plank}, 4, Tile.plank);
This would be an example of your crafting bench with an array instead of variables:
// This array resembles your crafting bench grid (2x2) in this case.
public static int[] arrayCraftingSlots = {Tile.plank, 0, 0, Tile.wood};
The function for shaped crafting:
/* int elements[] - takes in the crafting layout (this should equal your crafting bench grid)
* int amount - the amount of the item or tile you get as a result
* int result - the id of the result you get
*/
public static void shapedCrafting(int elements[], int amount, int result)
{
for(int i = 0; i < elements.length; i++)
{
if(arrayCraftingSlots[i] != elements[i])
{
return;
}
}
Inventory.inv_result.id = result;
Inventory.inv_result.blockNum += amount;
System.out.println("You can have " + amount + " " + result);
}
And the function for shapeless crafting:
I've restricted this one to 1 instance of an item per recipe just like in Minecraft. So using 2 planks in one shapeless recipe would not be possible. Of course you can change that if you want to. Keep in mind you have to change a bit of the algorithm as well.
/* int elements[] - list of elements that need to be anywhere in the crafting bench
* int amount - the amount of the item or tile you get as a result
* int result - the id of the result you get
*/
public static void shapelessCrafting(int elements[], int amount, int result)
{
for(int element : arrayCraftingSlots)
{
boolean in = false;
for(int i = 0; i < elements.length; i++)
{
if(element == elements[i] || element == 0)
{
in = true;
}
}
if(!in)
{
return;
}
}
for(int element : elements)
{
int occassions = 0;
for(int i = 0; i < arrayCraftingSlots.length; i++)
{
if(element == arrayCraftingSlots[i])
{
occassions++;
}
}
if(occassions > 1 || occassions == 0)
{
return;
}
}
Inventory.inv_result.id = result;
Inventory.inv_result.blockNum += amount;
System.out.println("You can have " + amount + " " + result);
}
Like the title says. I want to find out if a given java String contains an emoticon.
I can't use Character.UnicodeBlock.of(char) == Character.UnicodeBlock.EMOTICONS since that requires API level 19.
I found this code for iOS but it isn't really applicable since it looks like java and objective-c handle surrogate pairs in different manners.
The documentations I've looked through tell me that:
A char value, therefore, represents Basic Multilingual Plane (BMP) code points, including the surrogate code points, or code units of the UTF-16 encoding
I'm not quite sure what that means. Does that simply mean that they also include the BMP point as their first number?
According to Wikipedia the emoticon set lies between 0x1f600 and 0x1f64f but I don't know how to check if the char is in that range.
I had hoped that something like this would work but it didn't
if (0x1f600 <= a && a <= 0x1f64f)
{
Print.d("Unicode", "groovy!");
}
So how do I go about this?
Four years later...
At this time, it might make more sense to take advantage of EmojiCompat. This code presumes you initialized EmojiCompat when your app was starting up. The basic idea here is to have EmojiCompat process your CharSequence, inserting instances of EmojiSpan wherever any emoji appear, and then examine the results.
public static boolean containsEmoji(CharSequence charSequence) {
boolean result = false;
CharSequence processed = EmojiCompat.get().process(charSequence, 0, charSequence.length() -1, Integer.MAX_VALUE, EmojiCompat.REPLACE_STRATEGY_ALL);
if (processed instanceof Spannable) {
Spannable spannable = (Spannable) processed;
result = spannable.getSpans(0, spannable.length() - 1, EmojiSpan.class).length > 0;
}
return result;
}
If you want to collect a list of the unique emoji that appear within a given CharSequence, you could do something like this, iterating over the results of getSpans() and finding the start and end of each span to capture the emoji discovered by EmojiCompat:
#NonNull
public static List<String> getUniqueEmoji(CharSequence charSequence) {
Set<String> emojiList = new HashSet<>();
CharSequence processed = EmojiCompat.get().process(charSequence, 0, charSequence.length() -1, Integer.MAX_VALUE, EmojiCompat.REPLACE_STRATEGY_ALL);
if (processed instanceof Spannable) {
Spannable spannable = (Spannable) processed;
EmojiSpan[] emojiSpans = spannable.getSpans(0, spannable.length() - 1, EmojiSpan.class);
for (EmojiSpan emojiSpan : emojiSpans) {
int spanStart = spannable.getSpanStart(emojiSpan);
int spanEnd = spannable.getSpanEnd(emojiSpan);
CharSequence emojiCharSequence = spannable.subSequence(spanStart, spanEnd);
emojiList.add(String.valueOf(emojiCharSequence));
}
}
return emojiList.size() > 0 ? new ArrayList<>(emojiList) : new ArrayList<String>();
}
UPDATE: Here's an example of EmojiCompat initialization. This static method can be called from your Application onCreate() method, passing in the Application itself as the Context param.
#JvmStatic
fun initEmojiCompat(context: Context) {
if (emojiCompatConfig != null) {
// alternatively, EmojiCompat.reset() could be called here
logger().w(LOGTAG, "EmojiCompat already initialized.")
return
}
// "Noto Color Emoji Compat" doesn't have graphics for the following emojis:
// U+1F5E3 "speaking head" (required)
// U+1F441 "eye" (required)
// U+1F575 "detective" (nice to have)
val fontRequest = FontRequest(
"com.google.android.gms.fonts",
"com.google.android.gms",
"Noto Color Emoji Compat",
R.array.com_google_android_gms_fonts_certs
)
emojiCompatConfig = FontRequestEmojiCompatConfig(context, fontRequest)
.setReplaceAll(false)
.setEmojiSpanIndicatorEnabled(false)
.registerInitCallback(initCallback)
.also {
EmojiCompat.init(it)
}
}
I was in fact able to use the linked iOS code to create the following function. I didn't realize that a String that contains, for example, a single emoticon will have a length of 2. So you can check if a character is in fact a surrogate.
I'm not entirely sure how to handle else if (substring.length > 1) from the iOS code but I think Character.isHighSurrogate(myChar) does the same job in that instance.
private boolean containsIllegalCharacters(String displayName)
{
final int nameLength = displayName.length();
for (int i = 0; i < nameLength; i++)
{
final char hs = displayName.charAt(i);
if (0xd800 <= hs && hs <= 0xdbff)
{
final char ls = displayName.charAt(i + 1);
final int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000;
if (0x1d000 <= uc && uc <= 0x1f77f)
{
return true;
}
}
else if (Character.isHighSurrogate(hs))
{
final char ls = displayName.charAt(i + 1);
if (ls == 0x20e3)
{
return true;
}
}
else
{
// non surrogate
if (0x2100 <= hs && hs <= 0x27ff)
{
return true;
}
else if (0x2B05 <= hs && hs <= 0x2b07)
{
return true;
}
else if (0x2934 <= hs && hs <= 0x2935)
{
return true;
}
else if (0x3297 <= hs && hs <= 0x3299)
{
return true;
}
else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50)
{
return true;
}
}
}
return false;
}
This is how Telegram does it:
private static boolean isEmoji(String message){
return message.matches("(?:[\uD83C\uDF00-\uD83D\uDDFF]|[\uD83E\uDD00-\uD83E\uDDFF]|" +
"[\uD83D\uDE00-\uD83D\uDE4F]|[\uD83D\uDE80-\uD83D\uDEFF]|" +
"[\u2600-\u26FF]\uFE0F?|[\u2700-\u27BF]\uFE0F?|\u24C2\uFE0F?|" +
"[\uD83C\uDDE6-\uD83C\uDDFF]{1,2}|" +
"[\uD83C\uDD70\uD83C\uDD71\uD83C\uDD7E\uD83C\uDD7F\uD83C\uDD8E\uD83C\uDD91-\uD83C\uDD9A]\uFE0F?|" +
"[\u0023\u002A\u0030-\u0039]\uFE0F?\u20E3|[\u2194-\u2199\u21A9-\u21AA]\uFE0F?|[\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55]\uFE0F?|" +
"[\u2934\u2935]\uFE0F?|[\u3030\u303D]\uFE0F?|[\u3297\u3299]\uFE0F?|" +
"[\uD83C\uDE01\uD83C\uDE02\uD83C\uDE1A\uD83C\uDE2F\uD83C\uDE32-\uD83C\uDE3A\uD83C\uDE50\uD83C\uDE51]\uFE0F?|" +
"[\u203C\u2049]\uFE0F?|[\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE]\uFE0F?|" +
"[\u00A9\u00AE]\uFE0F?|[\u2122\u2139]\uFE0F?|\uD83C\uDC04\uFE0F?|\uD83C\uDCCF\uFE0F?|" +
"[\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA]\uFE0F?)+");
}
It is Line 21,026.
Try this...
if (Integer.parseInt("1f600", 16) <= (int)'☺' && (int)'☺' <= Integer.parseInt("1f64f", 16)) {
Print.d("Unicode", "groovy!");
}
This might work because the hexidecimal value and the char value are both being converted to ints.
Here's some Kotlin that relies on java.lang.Character api (granted the original poster can't use this). I have found it pretty reliably tells apart an emoji from 'special characters' and non-latin alphabets etc. Try it.
import java.lang.Character.*
import java.lang.Character.UnicodeBlock.MISCELLANEOUS_TECHNICAL
import java.lang.Character.UnicodeBlock.MISCELLANEOUS_SYMBOLS
import java.lang.Character.UnicodeBlock.VARIATION_SELECTORS
fun isStringEmoji(someString: String): Boolean {
if (someString.isNotEmpty() && someString.length < 5) {
val firstCodePoint = codePointAt(someString, 0)
val lastCodePoint = codePointBefore(someString, someString.length)
if (isValidCodePoint(firstCodePoint) && isValidCodePoint(lastCodePoint)) {
if (isSupplementaryCodePoint(firstCodePoint) ||
isSupplementaryCodePoint(lastCodePoint) ||
UnicodeBlock.of(firstCodePoint) == MISCELLANEOUS_SYMBOLS ||
UnicodeBlock.of(firstCodePoint) == MISCELLANEOUS_TECHNICAL ||
UnicodeBlock.of(lastCodePoint) == VARIATION_SELECTORS
) {
return true
}
}
}
return false
}