Highlighting using Regex in JSOUP for android - android

I am using JSoup parser to find particular parts of a html document (defined by regex) and highlight it by wrapping the found string in <span> tag. Here is my code that does the highlighting -
public String highlightRegex() {
Document doc = Jsoup.parse(htmlContent);
NodeTraversor nd = new NodeTraversor(new NodeVisitor() {
#Override
public void tail(Node node, int depth) {
if (node instanceof Element) {
Element elem = (Element) node;
StringBuffer obtainedText;
for(Element tn : elem.getElementsMatchingOwnText(pat)) {
Log.e("HELLO", tn.baseUri());
Log.e("HELLO", tn.text());
obtainedText = new StringBuffer(tn.ownText());
mat = pat.matcher(obtainedText.toString());
int nextStart = 0;
while(mat.find(nextStart)) {
obtainedText = obtainedText.replace(mat.start(), mat.end(), "<span>" + mat.group() + "</span>");
nextStart = mat.end() + 1;
}
tn.text(obtainedText.toString());
Log.e("HELLO" , "AFTER:" + tn.text());
}
}
}
#Override
public void head(Node node, int depth) {
}
});
nd.traverse(doc.body());
return doc.toString();
}
It does work but the tag <span> is visible inside the webview. What am I doing wrong?

Looks like no one knows. Here's some code that i've come up with. Slow and inefficient but works anyway. Suggestions are accepted :)
This class can be used to highlight any html using a regex.
public class Highlighter {
private String regex;
private String htmlContent;
Pattern pat;
Matcher mat;
public Highlighter(String searchString, String htmlString) {
regex = buildRegexFromQuery(searchString);
htmlContent = htmlString;
pat = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
}
public String getHighlightedHtml() {
Document doc = Jsoup.parse(htmlContent);
final List<TextNode> nodesToChange = new ArrayList<TextNode>();
NodeTraversor nd = new NodeTraversor(new NodeVisitor() {
#Override
public void tail(Node node, int depth) {
if (node instanceof TextNode) {
TextNode textNode = (TextNode) node;
String text = textNode.getWholeText();
mat = pat.matcher(text);
if(mat.find()) {
nodesToChange.add(textNode);
}
}
}
#Override
public void head(Node node, int depth) {
}
});
nd.traverse(doc.body());
for (TextNode textNode : nodesToChange) {
Node newNode = buildElementForText(textNode);
textNode.replaceWith(newNode);
}
return doc.toString();
}
private static String buildRegexFromQuery(String queryString) {
String regex = "";
String queryToConvert = queryString;
/* Clean up query */
queryToConvert = queryToConvert.replaceAll("[\\p{Punct}]*", " ");
queryToConvert = queryToConvert.replaceAll("[\\s]*", " ");
String[] regexArray = queryString.split(" ");
regex = "(";
for(int i = 0; i < regexArray.length - 1; i++) {
String item = regexArray[i];
regex += "(\\b)" + item + "(\\b)|";
}
regex += "(\\b)" + regexArray[regexArray.length - 1] + "[a-zA-Z0-9]*?(\\b))";
return regex;
}
private Node buildElementForText(TextNode textNode) {
String text = textNode.getWholeText().trim();
ArrayList<MatchedWord> matchedWordSet = new ArrayList<MatchedWord>();
mat = pat.matcher(text);
while(mat.find()) {
matchedWordSet.add(new MatchedWord(mat.start(), mat.end()));
}
StringBuffer newText = new StringBuffer(text);
for(int i = matchedWordSet.size() - 1; i >= 0; i-- ) {
String wordToReplace = newText.substring(matchedWordSet.get(i).start, matchedWordSet.get(i).end);
wordToReplace = "<b>" + wordToReplace+ "</b>";
newText = newText.replace(matchedWordSet.get(i).start, matchedWordSet.get(i).end, wordToReplace);
}
return new DataNode(newText.toString(), textNode.baseUri());
}
class MatchedWord {
public int start;
public int end;
public MatchedWord(int start, int end) {
this.start = start;
this.end = end;
}
}
}
you have to call these two methods to get the highlighted html -
Highlighter hl = new Highlighter("abc def", htmlString);
String newhtmlString = hl.getHighlightedHtml();
This will highlight everything that matches the regex (abc)|(def)*.
You can change the way you want the regex to be built by modifying buildRegexFromQuery() function.

Related

How do I convert full String 10 + 20 - 25; like text, got from a EditText, to double? [duplicate]

This question already has answers here:
Convert String to operator(+*/-) in java
(5 answers)
Closed 4 years ago.
How I convert String containing Mathematic arithmetic operation's like "10 + 20 - 25", I am getting String from EditText,I want to convert get the Result of operation.
Here is my code to resolve your problem:
public class ExecuteHandler {
private static Character[] OPERATORS = { '/', '*', '+', '-' };
private static final String REGEXOPERATORS = "[/+,-,/*,//,-]";
private static final String REGEXDIGITS = "(\\d+)";
private ArrayList<Character> operators = new ArrayList<>();
private ArrayList<Integer> digits = new ArrayList<>();
public String execute(String math) {
StringBuilder result = new StringBuilder();
try {
getDigits(math);
getOperators(math);
getNextOperator(operators);
for (Integer digit : digits) {
result.append(String.valueOf(digit));
}
} catch (ArithmeticException | IndexOutOfBoundsException e) {
return "ERROR";
}
return result.toString().isEmpty() ? "ERROR" : result.toString();
}
public void clear() {
operators.clear();
digits.clear();
}
private void getNextOperator(ArrayList<Character> operators) {
for (Character op : OPERATORS) {
for (int i = 0; i < operators.size(); i++) {
if (operators.get(i) == '/') {
operators.remove(i);
digits.set(i, (digits.get(i) / digits.get(i + 1)));
digits.remove(i + 1);
i -= 1;
}
}
for (int i = 0; i < operators.size(); i++) {
if (operators.get(i) == '*') {
operators.remove(i);
digits.set(i, (digits.get(i) * digits.get(i + 1)));
digits.remove(i + 1);
i -= 1;
}
}
for (int i = 0; i < operators.size(); i++) {
if (operators.get(i) == '+') {
operators.remove(i);
digits.set(i, (digits.get(i) + digits.get(i + 1)));
digits.remove(i + 1);
i -= 1;
}
}
for (int i = 0; i < operators.size(); i++) {
if (operators.get(i) == '-') {
operators.remove(i);
digits.set(i, (digits.get(i) - digits.get(i + 1)));
digits.remove(i + 1);
i -= 1;
}
}
}
}
private void getDigits(String math) {
Pattern r = Pattern.compile(REGEXDIGITS);
Matcher m = r.matcher(math);
while (m.find()) {
int t = Integer.parseInt(math.substring(m.start(), m.end()));
digits.add(t);
}
}
private void getOperators(String math) {
Pattern r = Pattern.compile(REGEXOPERATORS);
Matcher m = r.matcher(math);
while (m.find()) {
operators.add(math.charAt(m.start()));
}
}
}
Call method execute with input is string like "10 + 20 - 25:", the result will be a string of value (if success) or ERROR (if any syntax error).

How can I convert numbers to currency format in android

I want to show my numbers in money format and separate digits like the example below:
1000 -----> 1,000
10000 -----> 10,000
100000 -----> 100,000
1000000 -----> 1,000,000
Thanks
Another approach :
NumberFormat format = NumberFormat.getCurrencyInstance();
format.setMaximumFractionDigits(0);
format.setCurrency(Currency.getInstance("EUR"));
format.format(1000000);
This way, it's displaying 1 000 000 € or 1,000,000 €, depending on device currency's display settings
You need to use a number formatter, like so:
NumberFormat formatter = new DecimalFormat("#,###");
double myNumber = 1000000;
String formattedNumber = formatter.format(myNumber);
//formattedNumber is equal to 1,000,000
Hope this helps!
double number = 1000000000.0;
String COUNTRY = "US";
String LANGUAGE = "en";
String str = NumberFormat.getCurrencyInstance(new Locale(LANGUAGE, COUNTRY)).format(number);
//str = $1,000,000,000.00
Currency formatter.
public static String currencyFormat(String amount) {
DecimalFormat formatter = new DecimalFormat("###,###,##0.00");
return formatter.format(Double.parseDouble(amount));
}
Use this:
int number = 1000000000;
String str = NumberFormat.getNumberInstance(Locale.US).format(number);
//str = 1,000,000,000
This Method gives you the exact output which you need:
public String currencyFormatter(String num) {
double m = Double.parseDouble(num);
DecimalFormat formatter = new DecimalFormat("###,###,###");
return formatter.format(m);
}
Try the following solution:
NumberFormat format = NumberFormat.getCurrencyInstance();
((TextView)findViewById(R.id.text_result)).setText(format.format(result));
The class will return a formatter for the device default currency.
You can refer to this link for more information:
https://developer.android.com/reference/java/text/NumberFormat.html
Here's a kotlin Extension that converts a Double to a Currency(Nigerian Naira)
fun Double.toRidePrice():String{
val format: NumberFormat = NumberFormat.getCurrencyInstance()
format.maximumFractionDigits = 0
format.currency = Currency.getInstance("NGN")
return format.format(this.roundToInt())
}
Use a Formatter class
For eg:
String s = (String.format("%,d", 1000000)).replace(',', ' ');
Look into:
http://developer.android.com/reference/java/util/Formatter.html
The way that I do this in our app is this:
amount.addTextChangedListener(new CurrencyTextWatcher(amount));
And the CurrencyTextWatcher is this:
public class CurrencyTextWatcher implements TextWatcher {
private EditText ed;
private String lastText;
private boolean bDel = false;
private boolean bInsert = false;
private int pos;
public CurrencyTextWatcher(EditText ed) {
this.ed = ed;
}
public static String getStringWithSeparator(long value) {
DecimalFormat formatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
String f = formatter.format(value);
return f;
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
bDel = false;
bInsert = false;
if (before == 1 && count == 0) {
bDel = true;
pos = start;
} else if (before == 0 && count == 1) {
bInsert = true;
pos = start;
}
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
lastText = s.toString();
}
#Override
public void afterTextChanged(Editable s) {
ed.removeTextChangedListener(this);
StringBuilder sb = new StringBuilder();
String text = s.toString();
for (int i = 0; i < text.length(); i++) {
if ((text.charAt(i) >= 0x30 && text.charAt(i) <= 0x39) || text.charAt(i) == '.' || text.charAt(i) == ',')
sb.append(text.charAt(i));
}
if (!sb.toString().equals(s.toString())) {
bDel = bInsert = false;
}
String newText = getFormattedString(sb.toString());
s.clear();
s.append(newText);
ed.addTextChangedListener(this);
if (bDel) {
int idx = pos;
if (lastText.length() - 1 > newText.length())
idx--; // if one , is removed
if (idx < 0)
idx = 0;
ed.setSelection(idx);
} else if (bInsert) {
int idx = pos + 1;
if (lastText.length() + 1 < newText.length())
idx++; // if one , is added
if (idx > newText.length())
idx = newText.length();
ed.setSelection(idx);
}
}
private String getFormattedString(String text) {
String res = "";
try {
String temp = text.replace(",", "");
long part1;
String part2 = "";
int dotIndex = temp.indexOf(".");
if (dotIndex >= 0) {
part1 = Long.parseLong(temp.substring(0, dotIndex));
if (dotIndex + 1 <= temp.length()) {
part2 = temp.substring(dotIndex + 1).trim().replace(".", "").replace(",", "");
}
} else
part1 = Long.parseLong(temp);
res = getStringWithSeparator(part1);
if (part2.length() > 0)
res += "." + part2;
else if (dotIndex >= 0)
res += ".";
} catch (Exception ex) {
ex.printStackTrace();
}
return res;
}
Now if you add this watcher to your EditText, as soon as user enter his number, the watcher decides whether it needs separator or not.
i used this code for my project and it works:
EditText edt_account_amount = findViewById(R.id.edt_account_amount);
edt_account_amount.addTextChangedListener(new DigitFormatWatcher(edt_account_amount));
and defined class:
public class NDigitCardFormatWatcher implements TextWatcher {
EditText et_filed;
String processed = "";
public NDigitCardFormatWatcher(EditText et_filed) {
this.et_filed = et_filed;
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void afterTextChanged(Editable editable) {
String initial = editable.toString();
if (et_filed == null) return;
if (initial.isEmpty()) return;
String cleanString = initial.replace(",", "");
NumberFormat formatter = new DecimalFormat("#,###");
double myNumber = new Double(cleanString);
processed = formatter.format(myNumber);
//Remove the listener
et_filed.removeTextChangedListener(this);
//Assign processed text
et_filed.setText(processed);
try {
et_filed.setSelection(processed.length());
} catch (Exception e) {
// TODO: handle exception
}
//Give back the listener
et_filed.addTextChangedListener(this);
}
}
Updated 2022 answer
Try this snippet. It formats a number in string complete with the currency & setting fractional digits.
Upvote if this helped you! :)
/**
* Formats amount in string to human-readable amount (separated with commas
* & prepends currency symbol)
*
* #param amount The amount to format in String
* #return The formatted amount complete with separators & currency symbol added
*/
public static String formatCurrency(String amount) {
String formattedAmount = amount;
try {
if (amount == null || amount.isEmpty())
throw new Exception("Amount is null/empty");
Double amountInDouble = Double.parseDouble(amount);
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(new Locale("en", "IN"));
numberFormat.setMaximumFractionDigits(2);
numberFormat.setMinimumFractionDigits(2);
formattedAmount = numberFormat.format(amountInDouble);
} catch (Exception exception) {
exception.printStackTrace();
return formattedAmount;
}
return formattedAmount;
}
private val currencyFormatter = NumberFormat.getCurrencyInstance(LOCALE_AUS).configure()
private fun NumberFormat.configure() = apply {
maximumFractionDigits = 2
minimumFractionDigits = 2
}
fun Number.asCurrency(): String {
return currencyFormatter.format(this)
}
And then just use as
val x = 100000.234
x.asCurrency()
If you have the value stored in a String like me, which was coming from the server like "$20000.00".
You can do something like this in Kotlin (JetpackCompose):
#Composable
fun PrizeAmount(
modifier: Modifier = Modifier,
prize: String,
)
{
val currencyFormat = NumberFormat.getCurrencyInstance(Locale("en", "US"))
val text = currencyFormat.format(prize.substringAfter("$").toDouble())
...
}
Output: "$20,000.00"
NumberFormat.getCurrencyInstance(Locale("ES", "es")).format(number)
here is a kotlin version to Format Currency, here i'm getting an argument from another fragment from an input Field then it will be set in the textView in the main Fragment
fun formatArgumentCurrency(argument : String, textView: TextView) {
val valueText = requireArguments().get(argument).toString()
val dec = DecimalFormat("#,###.##")
val number = java.lang.Double.valueOf(valueText)
val value = dec.format(number)
val currency = Currency.getInstance("USD")
val symbol = currency.symbol
textView.text = String.format("$symbol$value","%.2f" )
}
You can easily achieve this with this small simple library.
https://github.com/jpvs0101/Currencyfy
Just pass any number, then it will return formatted string, just like that.
currencyfy (500000.78); // $ 500,000.78 //default
currencyfy (500000.78, false); // $ 500,001 // hide fraction (will round off automatically!)
currencyfy (500000.78, false, false); // 500,001 // hide fraction & currency symbol
currencyfy (new Locale("en", "in"), 500000.78); // ₹ 5,00,000.78 // custom locale
It compatible with all versions of Android including older versions!

IndexOutOfRangeException: Array index is out of range. DataCache.GetAchievementCacheData () (at Assets/Scripts/Mission/Plugin/DataCache.cs:329)

When I run the game using unity I get this error continuously in the console window and the loading screen is not going further next. Help me to fix this issue.
IndexOutOfRangeException: Array index is out of range.
DataCache.GetAchievementCacheData () (at
Assets/Scripts/Mission/Plugin/DataCache.cs:329)
Here is my code below
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using UnityEngine;
//Class luu mission current
public class CurrentMission
{
public string FB_id { get; set; }
public string Name { get; set; }
public string Mission { get; set; }
public CurrentMission(string id, string name, string mission)
{
this.FB_id = id;
this.Name = name;
this.Mission = mission;
}
public CurrentMission()
{
this.FB_id = "";
this.Name = "";
this.Mission = "";
}
}
public class MissionDataSave
{
public int Mission;
public long Score;
public int Star;
public int Open;//0-false. 1-true
public MissionDataSave(int mission, long score, int star, int open)
{
Mission = mission;
Score = score;
Star = star;
Open = open;
}
public MissionDataSave()
{
Mission = 0;
Score = 0;
Star = 0;
Open = 0;
}
}
public class AchievementCache
{
//group nhiem vu
public int Group;
//Level hien tai cua group
public int Level;
//Gia tri hien tai
public int Value;
//Thong bao mission hoan thanh
public int Notify;//0 - False, 1 - true
//public AchievementCache()
//{
// this.Group = 1;
// this.Level = 1;
// this.Value = 0;
//}
public AchievementCache(int group, int level, int value, int notify)
{
this.Group = group;
this.Value = value;
this.Level = level;
this.Notify = notify;
}
public AchievementCache()
{
this.Group = 1;
this.Value = 0;
this.Level = 1;
this.Notify = 0;
}
}
public class DataCache
{
public static string FB_ID = "FB_ID";
public static string FB_USER = "FB_USER";
public static string Achievement_data_key = "Achievement_data_key";
public static string Mission_data_key = "Mission_data_key";
public static string Current_mission_data_key = "Current_mission_data_key";
public static AchievementCache[] dataAchievementCache;
public static MissionDataSave[] dataMissionCache;
public static CurrentMission[] dataCurrentMissionCache;
//public static string XML_Current_Mission_Path = "CurrentMissionSave.xml";
//public static string XML_Data_Mission_Path = "DataMissionSave.xml";
//public static string XML_Data_Achievement_Path = "AchievementCache.xml";
//serialize xml theo tung phan tu
//public static List<MissionDataSave> DeserializeMissionDataSaveListFromXML(string filePath)
//{
// if (!System.IO.File.Exists(filePath))
// {
// Debug.LogError("File " + filePath + " not exist!");
// return new List<MissionDataSave>();
// }
// XmlSerializer deserializer = new XmlSerializer(typeof(List<MissionDataSave>), new XmlRootAttribute("MissionDataSaveRoot"));
// TextReader textReader = new StreamReader(filePath);
// List<MissionDataSave> movies = (List<MissionDataSave>)deserializer.Deserialize(textReader);
// textReader.Close();
// return movies;
//}
//public static void readXMLTest()
//{
// string xmlDataCache1 = Application.persistentDataPath + "/" + XML_Current_Mission_Path;
// TextReader textReader = new StreamReader(xmlDataCache1);
// XmlDocument xmlDoc = new XmlDocument();
// xmlDoc.Load(textReader);
// XmlNodeList xmlNodeList = xmlDoc.DocumentElement.ChildNodes;
// Debug.Log("TRUOC");
// foreach (XmlNode node in xmlNodeList)
// {
// Debug.Log("aaaaaaaaaaaaaaaaaaaaa " + node.Attributes["Id"].Value);
// }
// Debug.Log("SAU");
// XmlNode root = xmlDoc.DocumentElement;
// XmlElement elem = xmlDoc.CreateElement("CurrentMissionCache");
// elem.SetAttribute("Id", "112312");
// elem.SetAttribute("Name", "NameDG");
// elem.SetAttribute("Mission", "MissionDG");
// root.AppendChild(elem);
// textReader.Close();
// xmlDoc.Save(xmlDataCache1);
//}
//Add mission xml node
public static void UpdateMissionScore(long score, int star, int mission, int open)
{
MissionDataSave data = dataMissionCache[mission - 1];
if (data.Star < star)
{
data.Star = star;
}
if (data.Score < score)
{
data.Score = score;
}
data.Open = open;
}
public static void SaveMissionDataCache(bool submitToServer = false)
{
string dataSave = "";
string dataSendServer = "";
for (int i = 0; i < dataMissionCache.Length; i++)
{
dataSave += dataMissionCache[i].Mission + "-" + dataMissionCache[i].Score + "-" + dataMissionCache[i].Star + "-" + dataMissionCache[i].Open + ",";
//Chi gui nhung mission da open len server
if (dataMissionCache[i].Open == 1)
{
if (dataSendServer.Length > 0)
dataSendServer += ",";
dataSendServer += dataMissionCache[i].Mission + "-" + dataMissionCache[i].Score + "-" + dataMissionCache[i].Star + "-" + dataMissionCache[i].Open;
}
}
Debug.Log("Data save " + dataSave);
PlayerPrefs.SetString(Mission_data_key, dataSave);
if (submitToServer)
{
Debug.Log("Data send server " + dataSendServer);
AudioControl.getMonoBehaviour().StartCoroutine(DHS.PostMeInfoMissionUpdate(FB.UserId, dataSendServer));
}
}
public static void GetMissionDataCache()
{
int max_mission = 100;
if (dataMissionCache != null)
{
dataMissionCache = null;
}
dataMissionCache = new MissionDataSave[max_mission];
//Tao moi neu chua co
if (!PlayerPrefs.HasKey(Mission_data_key))
{
string datas = "1-0-0-1,";
for (int i = 2; i <= max_mission; i++)
{
//Mission - Score - Star - Open
if (DataMissionControlNew.test)
{
datas += i + "-0-0-1,";
//if (i < 16)
// datas += i + "-0-0-1,";
//else datas += i + "-0-0-0,";
}
else
{
datas += i + "-0-0-0,";
}
}
PlayerPrefs.SetString(Mission_data_key, datas);
}
string missionData = PlayerPrefs.GetString(Mission_data_key);
string[] data = missionData.Split(',');
for (int i = 0; i < max_mission; i++)
{
string[] infoData = data[i].Split('-');
//Debug.Log("Info " + data[i]);
string mission = infoData[0];
string score = infoData[1];
string star = infoData[2];
string open = infoData[3];
dataMissionCache[i] = new MissionDataSave(Convert.ToUInt16(mission), Convert.ToUInt32(score), Convert.ToUInt16(star), Convert.ToUInt16(open));
}
}
//-------------------------CURRENT MISSION---------------------------
//Add current mission xml node
public static void SaveCurrentMission(string data = "")
{
if (String.IsNullOrEmpty(data))
{
string dataSave = "";
for (int i = 0; i < dataCurrentMissionCache.Length; i++)
{
if (dataSave.Length > 0)
dataSave += ",";
dataSave += dataCurrentMissionCache[i].FB_id + "-" + dataCurrentMissionCache[i].Name + "-" + dataCurrentMissionCache[i].Mission;
}
PlayerPrefs.SetString(Current_mission_data_key, dataSave);
}
else
{
PlayerPrefs.SetString(Current_mission_data_key, data);
GetCurrentMission();
}
}
public static void GetCurrentMission()
{
if (!PlayerPrefs.HasKey(Current_mission_data_key))
{
PlayerPrefs.SetString(Current_mission_data_key, "Me-User-1");
}
if (dataCurrentMissionCache != null)
{
dataCurrentMissionCache = null;
}
string current_data = PlayerPrefs.GetString(Current_mission_data_key);
string[] data = current_data.Split(',');
dataCurrentMissionCache = new CurrentMission[data.Length];
for (int i = 0; i < data.Length; i++)
{
string[] info = data[i].Split('-');
//fb - User name - missison
dataCurrentMissionCache[i] = new CurrentMission(info[0], info[1], info[2]);
}
}
public static void SetMeCurrentMission(int mission)
{
for (int i = 0; i < DataCache.dataCurrentMissionCache.Length; i++)
{
if ("Me".Equals(DataCache.dataCurrentMissionCache[i].FB_id))
{
int old = Convert.ToInt16(DataCache.dataCurrentMissionCache[i].Mission);
if (old < mission)
{
DataCache.dataCurrentMissionCache[i].Mission = "" + mission;
DataCache.UpdateMissionScore(0, 0, mission, 1);//Them mission moi vao xml
}
}
}
DataCache.SaveCurrentMission();
}
//-------------------------ACHIEVEMENT---------------------------
//Ghi de len du lieu cu
public static void ReplaceAchievementCache(int groupLevel, int value, int level = -1)
{
dataAchievementCache[groupLevel - 1].Value = value;
if (level != -1)
{
dataAchievementCache[groupLevel - 1].Level = level;
}
}
//Cap nhat them du lieu
public static void AddAchievementCache(int groupLevel, int addValue, int addLevel = 0)
{
dataAchievementCache[groupLevel - 1].Level += addLevel;
dataAchievementCache[groupLevel - 1].Value += addValue;
}
public static void GetAchievementCacheData()
{
Debug.Log("-------------GetAchievementCacheData--------------------");
if (dataAchievementCache != null)
{
dataAchievementCache = null;
}
dataAchievementCache = new AchievementCache[22];
//Tao achievement
if (!PlayerPrefs.HasKey(Achievement_data_key))
{
string achi = "";
for (int i = 1; i <= 22; i++)
{
achi += i + "-1-0-0,";
}
//Debug.Log("Create new achievement " + achi);
PlayerPrefs.SetString(Achievement_data_key, achi);
}
string achievement = PlayerPrefs.GetString(Achievement_data_key);
//Debug.Log(achievement);
string[] achie = achievement.Split(',');
for (int i = 0; i< dataAchievementCache.Length; i++)
{
//Debug.Log(achie[i]);
string[] infoAchie = achie[i].Split('-');
string group = infoAchie[0];
string level = infoAchie[1];
string value = infoAchie[2];
string notify = infoAchie[3];
//Debug.Log(group +" " + dataAchievementCache[i].Group);
dataAchievementCache[i] = new AchievementCache();
dataAchievementCache[i].Group = Convert.ToInt16(group);
dataAchievementCache[i].Level = Convert.ToInt16(level);
dataAchievementCache[i].Value = Convert.ToInt32(value);
dataAchievementCache[i].Notify = Convert.ToInt16(notify);
}
}
public static void SaveAchievementCache(bool sendServer = false)
{
try
{
Debug.Log("-------------------SaveAchievementCache-----------------");
if (dataAchievementCache != null)
{
string achievement = "";
for (int i = 0; i < dataAchievementCache.Length; i++)
{
string s = "" + dataAchievementCache[i].Group + "-" + dataAchievementCache[i].Level + "-" + dataAchievementCache[i].Value + "-" + dataAchievementCache[i].Notify + ",";
achievement += s;
}
//Debug.Log("----------LUU ACHIEVEMENT------------ " + achievement);
PlayerPrefs.SetString(Achievement_data_key, achievement);
if (FB.IsLoggedIn && sendServer)
{
//Nếu chưa có playerprefs thì sẽ submit lên luôn
//Nếu có rồi thì phải check nó cập nhật hoàn thành từ server về thì mới cho up lên
bool check = !PlayerPrefs.HasKey(DataMissionControlNew.key_update_achievement_data_from_server) ||
(PlayerPrefs.HasKey(DataMissionControlNew.key_update_achievement_data_from_server) && PlayerPrefs.GetInt(DataMissionControlNew.key_update_achievement_data_from_server) == 1);
if (check)
{
AudioControl.getMonoBehaviour().StartCoroutine(DHS.PostMeInfoUpdate(DFB.UserId, "" + VariableSystem.diamond, "" + achievement, "", (www) =>
{
Debug.Log("----------Update achievement to server success!------------- " + achievement);
}));
}
else
{
Debug.Log("----------KHONG CHO UP ACHIEVEMENT VA DIAMOND LEN SERVER------------- " + PlayerPrefs.GetInt(DataMissionControlNew.key_update_mission_data_from_server, 0));
}
}
}
}
catch (Exception e)
{
Debug.Log("------------ERROR ---------------" + e.Message);
if (DataMissionControlNew.test)
{
MobilePlugin.getInstance().ShowToast("ERROR " + e.Message);
}
}
}
public static void DeleteUserData()
{
PlayerPrefs.DeleteKey(FB_ID);
PlayerPrefs.DeleteKey(Mission_data_key);
PlayerPrefs.DeleteKey(Current_mission_data_key);
PlayerPrefs.DeleteKey(Achievement_data_key);
PlayerPrefs.DeleteKey("diamond");
PlayerPrefs.DeleteKey(DataMissionControlNew.key_update_mission_data_from_server);
VariableSystem.diamond = 8;
VariableSystem.heart = 5;
}
public static void RestoreUserData(int diamond, string achievement)
{
Debug.Log("Restore user data");
VariableSystem.diamond = diamond;
VariableSystem.heart = PlayerPrefs.GetInt("heart", 5);
string[] achie = achievement.Split(',');
if (achie.Length > 5)
{
for (int i = 0; i < dataAchievementCache.Length; i++)
{
string[] infoAchie = achie[i].Split('-');
string group = infoAchie[0];
string level = infoAchie[1];
string value = infoAchie[2];
string notify = infoAchie[3];
dataAchievementCache[i].Group = Convert.ToInt16(group);
dataAchievementCache[i].Level = Convert.ToInt16(level);
dataAchievementCache[i].Value = Convert.ToInt32(value);
dataAchievementCache[i].Notify = Convert.ToInt16(notify);
}
//Debug.Log("---Luu achievement----");
SaveAchievementCache();
GetAchievementCacheData();
}
Debug.Log("----------------ACHIEVEMENT da dc cap nhat tu -----------------");
PlayerPrefs.SetInt(DataMissionControlNew.key_update_achievement_data_from_server, 1);
}
The line 329 is this
string level = infoAchie[1];
The problem is coming from the last comma you are adding in the string you are adding to your PlayerPrefs in this part:
if (!PlayerPrefs.HasKey(Achievement_data_key))
{
string achi = "";
for (int i = 1; i <= 22; i++)
{
achi += i + "-1-0-0,";
}
//Debug.Log("Create new achievement " + achi);
PlayerPrefs.SetString(Achievement_data_key, achi);
}
This code generates this string:
1-1-0-0,2-1-0-0,3-1-0-0,4-1-0-0,5-1-0-0,6-1-0-0,7-1-0-0,8-1-0-0,9-1-0-0,10-1-0-0,11-1-0-0,12-1-0-0,13-1-0-0,14-1-0-0,15-1-0-0,16-1-0-0,17-1-0-0,18-1-0-0,19-1-0-0,20-1-0-0,21-1-0-0,22-1-0-0,
Keep in memory that last comma at the end of the string.
You are later doing a split on the ',' character, and iterating over them.
string[] achie = achievement.Split(',');
for (int i = 0; i< dataAchievementCache.Length; i++)
{
//Debug.Log(achie[i]);
string[] infoAchie = achie[i].Split('-');
string group = infoAchie[0];
string level = infoAchie[1];
...
}
The problem is that by doing so, your achie string array contains an empty last element. So when you are later splitting on the "-" character, your infoAchie string array only contains one element: the empty string. The first line:
string group = infoAchie[0];
Still works, but is filled with the empty string. Then:
string level = infoAchie[1];
Can't work, as you are indeed out of the bounds of the infoAchie array.
The solution would be to add the comma to your string only if it is not the last element.
Also, I strongly advise you to use a StringBuilder over a simple string for optimization purposes. Your code could then look like, for instance:
if (!PlayerPrefs.HasKey(Achievement_data_key))
{
StringBuilder achi = new StringBuilder();
for (int i = 1; i <= 22; i++)
{
achi.Append(i).Append("-1-0-0");
if(i != 22)
achi.Append(",");
}
//Debug.Log("Create new achievement " + achi);
PlayerPrefs.SetString(Achievement_data_key, achi.ToString());
}
Which generates this string:
1-1-0-0,2-1-0-0,3-1-0-0,4-1-0-0,5-1-0-0,6-1-0-0,7-1-0-0,8-1-0-0,9-1-0-0,10-1-0-0,11-1-0-0,12-1-0-0,13-1-0-0,14-1-0-0,15-1-0-0,16-1-0-0,17-1-0-0,18-1-0-0,19-1-0-0,20-1-0-0,21-1-0-0,22-1-0-0

Android Html.fromHtml(String) doesnt work

I want to set String with HTML tags effects. By using following method I am not able to do that. Its showing me normal text.
#SuppressWarnings("deprecation")
public static Spanned fromHtml(String html){
Spanned result;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
result = Html.fromHtml(html,Html.FROM_HTML_MODE_LEGACY);
} else {
result = Html.fromHtml(html);
}
return result;
}
I am passing following string to function;
vgdgffdgdgfdfgdfgdfgdfgdfg..........aererwerewrwerweryyiyuiuuyuuyiyiuy
hjjgjhghghgjhgjhgjgtttggtttghgggggg
But when I apply Html.fromHtml(html); its return following string
vgdgffdgdgfdfgdfgdfgdfgdfg.......... aererwerewrwerwer yyiyuiuuyuuyiyiuy hjjgjhghghgjhgjhgj gtttggtttghgggggg
I am running my app in emulator with API 23
Please provide some solution to handle HTML tags.
Android support some Html tags. you can see supported tags by android.
Supproted Html Tag by Android
Android not support ul and li tags. for that you have to handle tags like below
public class HtmlTagHandler implements Html.TagHandler {
boolean first = true;
String parent = null;
int index = 1;
private int mListItemCount = 0;
private Vector<String> mListParents = new Vector<String>();
#Override
public void handleTag(final boolean opening, final String tag, Editable output, final XMLReader xmlReader) {
if (tag.equals("ul") || tag.equals("ol") || tag.equals("dd")) {
if (opening) {
mListParents.add(tag);
} else mListParents.remove(tag);
mListItemCount = 0;
} else if (tag.equals("li") && !opening) {
handleListTag(output);
} else if (tag.equalsIgnoreCase("code")) {
if (opening) {
output.setSpan(new TypefaceSpan("monospace"), output.length(), output.length(), Spannable.SPAN_MARK_MARK);
} else {
Log.d("COde Tag", "Code tag encountered");
Object obj = getLast(output, TypefaceSpan.class);
int where = output.getSpanStart(obj);
output.setSpan(new TypefaceSpan("monospace"), where, output.length(), 0);
}
}
}
private Object getLast(Editable text, Class kind) {
Object[] objs = text.getSpans(0, text.length(), kind);
if (objs.length == 0) {
return null;
} else {
for (int i = objs.length; i > 0; i--) {
if (text.getSpanFlags(objs[i - 1]) == Spannable.SPAN_MARK_MARK) {
return objs[i - 1];
}
}
return null;
}
}
private void handleListTag(Editable output) {
if (mListParents.lastElement().equals("ul")) {
output.append("\n");
String[] split = output.toString().split("\n");
int lastIndex = split.length - 1;
int start = output.length() - split[lastIndex].length() - 1;
output.setSpan(new BulletSpan(15 * mListParents.size()), start, output.length(), 0);
} else if (mListParents.lastElement().equals("ol")) {
mListItemCount++;
output.append("\n");
String[] split = output.toString().split("\n");
int lastIndex = split.length - 1;
int start = output.length() - split[lastIndex].length() - 1;
output.insert(start, mListItemCount + ". ");
output.setSpan(new LeadingMarginSpan.Standard(15 * mListParents.size()), start, output.length(), 0);
}
}
}
Now you just have to create list item string with ul or li tag like below. In this method you just have to pass array of strings which you want to show as html list.
public static String getHtmlText(String[] s) {
String ulStart = "<ul>";
for (int i = 0; i < s.length; i++) {
ulStart += "<li>" + s[i] + "</li>";
}
ulStart += "</ul>";
return ulStart;
}
You can use like this:
textview.setText(Html.fromHtml(htmlString, null, new HtmlTagHandler()));

How can I parse price from Google Play In-app Billing

I use the follow code to parse price from Google Play In-app Billing:
private static Number parsePrice(String priceFromGoogle) {
Locale currencyLocale = getCurrencyLocale(priceFromGoogle);
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(currencyLocale);
Number number = null;
try {
number = numberFormat.parse(priceFromGoogle);
} catch (ParseException e) {
e.printStackTrace();
}
return number;
}
private Locale getCurrencyLocale(String price) {
Locale locale = null;
for (Locale availableLocale : Locale.getAvailableLocales()) {
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(availableLocale);
try {
numberFormat.parse(price);
locale = availableLocale;
break;
} catch (ParseException e) {
//do nothing
}
}
return locale;
}
It works fine on my test devices and in my locale. But on some devices and in some countries I encounter prices like this: "Php1,337.07", "US$ 29.99", "MX$374.79". My approach doesn't work in this case.
Is there an universal approach to solve this problem?
Check their In-app billing sample project and modify SkuDetails.java so that you can get that information as well:
import org.json.JSONException;
import org.json.JSONObject;
/**
* Represents an in-app product's listing details.
*/
public class SkuDetails {
String mItemType;
String mSku;
String mType;
int mPriceAmountMicros;
String mPriceCurrencyCode;
String mPrice;
String mTitle;
String mDescription;
String mJson;
public SkuDetails(String jsonSkuDetails) throws JSONException {
this(IabHelper.ITEM_TYPE_INAPP, jsonSkuDetails);
}
public SkuDetails(String itemType, String jsonSkuDetails) throws JSONException {
mItemType = itemType;
mJson = jsonSkuDetails;
JSONObject o = new JSONObject(mJson);
mSku = o.optString("productId");
mType = o.optString("type");
mPrice = o.optString("price");
mPriceAmountMicros = o.optInt("price_amount_micros");
mPriceCurrencyCode = o.optString("price_currency_code");
mTitle = o.optString("title");
mDescription = o.optString("description");
}
public String getSku() { return mSku; }
public String getType() { return mType; }
public String getPrice() { return mPrice; }
public String getTitle() { return mTitle; }
public String getDescription() { return mDescription; }
public int getPriceAmountMicros() { return mPriceAmountMicros; }
public String getPriceCurrencyCode() { return mPriceCurrencyCode; }
#Override
public String toString() {
return "SkuDetails:" + mJson;
}
}
You can get the price in micros in the JSON retrieved by IabHelper.
This is not officially documented but here is how I've done it by editing SkuDetails.java :
public class SkuDetails {
...
Double mPriceMicros;
public SkuDetails(String itemType, String jsonSkuDetails) throws JSONException {
...
String priceMicros = o.optString("price_amount_micros");
if (priceMicros != null) {
String format = new StringBuilder(priceMicros).insert(priceMicros.length() - 6, ".").toString();
mPriceMicros = Double.parseDouble(format);
}
}
...
public Double getPriceMicros() { return mPriceMicros; }
}
Hope this helps !
PS : I tried your Price class but it parsed 0.8 for 0,89 €
As Google Play may return prices in currency format which is unsupported by java.text.NumberFormat, I wrote my own implementation
public class Price {
private double value;
private String currency;
private String pattern;
private DecimalFormat decimalFormat;
private Price() {}
private static String currencyToDecimalFormat(String value, Price price) {
char decimalSeparator = '.';
char groupingSeparator = 0;
if (value.length() >= 3) {
char[] chars = value.toCharArray();
if (chars[chars.length - 2] == ',') {
decimalSeparator = ',';
chars[chars.length - 2] = '.';
} else if (chars[chars.length - 3] == ',') {
decimalSeparator = ',';
chars[chars.length - 3] = '.';
}
value = new String(chars);
}
if (value.contains(",")) {
groupingSeparator = ',';
value = value.replaceAll(",", "");
} else if (value.contains(" ")) {
groupingSeparator = ' ';
value = value.replaceAll(" ", "");
} else if (value.contains("\u00A0")) {
groupingSeparator = '\u00A0';
value = value.replaceAll("\u00A0", "");
}
DecimalFormatSymbols symbols = new DecimalFormatSymbols();
if (groupingSeparator != 0) {
price.decimalFormat = new DecimalFormat("###,###.00");
symbols.setGroupingSeparator(groupingSeparator);
} else {
price.decimalFormat = new DecimalFormat("######.00");
}
symbols.setDecimalSeparator(decimalSeparator);
price.decimalFormat.setDecimalFormatSymbols(symbols);
return value.replaceAll(",", "");
}
public static Price parsePrice(String priceFromGoogle) {
Price price = new Price();
StringBuilder patternBuilder = new StringBuilder();
Pattern pattern = Pattern.compile("(?:[0-9]{1,3})(?:[0-9,.\\s\u00A0]+)");
Matcher matcher = pattern.matcher(priceFromGoogle);
matcher.find();
String priceString = matcher.group();
if (priceFromGoogle.indexOf(priceString) == 0) {
if (priceFromGoogle.length() != priceString.length()) {
price.currency = priceFromGoogle.substring(priceString.length());
} else {
price.currency = "";
}
} else {
price.currency = priceFromGoogle.substring(0, priceFromGoogle.indexOf(priceString));
}
price.currency = price.currency.trim();
if (priceFromGoogle.startsWith(price.currency)) {
patternBuilder.append("%1s");
char nextChar = priceFromGoogle.charAt(price.currency.length());
if (nextChar == ' ' || nextChar == 0xA0) {
patternBuilder.append(' ');
}
patternBuilder.append("%2$s");
} else {
patternBuilder.append("%2$s");
char prevChar = priceFromGoogle.charAt(priceFromGoogle.indexOf(price.currency) - 1);
if (prevChar == ' ' || prevChar == 0xA0) {
patternBuilder.append(' ');
}
patternBuilder.append("%1s");
}
price.pattern = patternBuilder.toString();
priceString = trim(priceString);
priceString = currencyToDecimalFormat(priceString, price);
price.value = Double.parseDouble(priceString);
return price;
}
#Override
public String toString() {
if (pattern != null) {
return String.format(pattern, currency, decimalFormat.format(value));
} else {
return "";
}
}
}
EDIT1:
Because of Google uses non-breaking space instead of usual space you need check this and use custom trim function:
public static String trim(String text) {
int start = 0, last = text.length() - 1;
int end = last;
while ((start <= end) && (text.charAt(start) <= ' ' || text.charAt(start) == 0xA0)) {
start++;
}
while ((end >= start) && (text.charAt(end) <= ' ' || text.charAt(end) == 0xA0)) {
end--;
}
if (start == 0 && end == last) {
return text;
}
return text.substring(start, end);
}

Categories

Resources