mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2025-01-14 19:57:32 +01:00
commit
e67398af87
@ -42,9 +42,11 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.LanguageUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.RtlUtils;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.util.JavaExtensions.coalesce;
|
||||
|
||||
|
||||
public class GBDeviceService implements DeviceService {
|
||||
protected final Context mContext;
|
||||
private final Class<? extends Service> mServiceClass;
|
||||
@ -82,6 +84,14 @@ public class GBDeviceService implements DeviceService {
|
||||
}
|
||||
}
|
||||
|
||||
if (RtlUtils.rtlSupport()) {
|
||||
for (String extra : transliterationExtras) {
|
||||
if (intent.hasExtra(extra)) {
|
||||
intent.putExtra(extra, RtlUtils.fixRtl(intent.getStringExtra(extra)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mContext.startService(intent);
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ import java.util.Map;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
|
||||
public class LanguageUtils {
|
||||
|
||||
//transliteration map with english equivalent for unsupported chars
|
||||
private static Map<Character, String> transliterateMap = new HashMap<Character, String>(){
|
||||
{
|
||||
|
@ -0,0 +1,567 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.util;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
|
||||
public class RtlUtils {
|
||||
|
||||
/**
|
||||
* Checks the status of right-to-left option
|
||||
* @return true if right-to-left option is On, and false, if Off or not exist
|
||||
*/
|
||||
public static boolean rtlSupport()
|
||||
{
|
||||
return GBApplication.getPrefs().getBoolean("rtl", false);
|
||||
}
|
||||
|
||||
public enum characterType{
|
||||
ltr,
|
||||
rtl,
|
||||
rtl_arabic,
|
||||
punctuation,
|
||||
lineEnd,
|
||||
space,
|
||||
}
|
||||
|
||||
public static characterType getCharacterType(Character c){
|
||||
characterType type;
|
||||
switch (Character.getDirectionality(c)) {
|
||||
case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
|
||||
type = characterType.rtl;
|
||||
break;
|
||||
case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
|
||||
type = characterType.rtl_arabic;
|
||||
break;
|
||||
case Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR:
|
||||
case Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR:
|
||||
case Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR:
|
||||
case Character.DIRECTIONALITY_OTHER_NEUTRALS:
|
||||
type = characterType.punctuation;
|
||||
break;
|
||||
case Character.DIRECTIONALITY_BOUNDARY_NEUTRAL:
|
||||
case Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR:
|
||||
type = characterType.lineEnd;
|
||||
break;
|
||||
case Character.DIRECTIONALITY_WHITESPACE:
|
||||
type = characterType.space;
|
||||
break;
|
||||
default:
|
||||
type = characterType.ltr;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the status of right-to-left option
|
||||
* @return true if right-to-left option is On, and false, if Off or not exist
|
||||
*/
|
||||
public static boolean contextualSupport()
|
||||
{
|
||||
return GBApplication.getPrefs().getBoolean("contextualArabic", false);
|
||||
}
|
||||
|
||||
//map with brackets chars to change there direction
|
||||
private static Map<Character, Character> directionSignsMap = new HashMap<Character, Character>(){
|
||||
{
|
||||
put('(', ')'); put(')', '('); put('[', ']'); put(']', '['); put('{','}'); put('}','{');
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @return true if the char is in the rtl range, otherwise false
|
||||
*/
|
||||
public static boolean isHebrew(Character c){
|
||||
|
||||
return getCharacterType(c) == characterType.rtl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the char is in the rtl range, otherwise false
|
||||
*/
|
||||
public static boolean isArabic(Character c){
|
||||
|
||||
return getCharacterType(c) == characterType.rtl_arabic;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the char is in the rtl range, otherwise false
|
||||
*/
|
||||
public static boolean isLtr(Character c){
|
||||
|
||||
return getCharacterType(c) == characterType.ltr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the char is in the rtl range, otherwise false
|
||||
*/
|
||||
public static boolean isRtl(Character c){
|
||||
|
||||
return (getCharacterType(c) == characterType.rtl) || (getCharacterType(c) == characterType.rtl_arabic);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the char is in the punctuations range, otherwise false
|
||||
*/
|
||||
public static boolean isPunctuations(Character c){
|
||||
|
||||
return getCharacterType(c) == characterType.punctuation;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return true if the char is in the end of word list, otherwise false
|
||||
*/
|
||||
public static boolean isSpaceSign(Character c){
|
||||
|
||||
return getCharacterType(c) == characterType.space;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the char is in the end of word list, otherwise false
|
||||
*/
|
||||
public static boolean isEndLineSign(Character c){
|
||||
|
||||
return getCharacterType(c) == characterType.lineEnd;
|
||||
}
|
||||
|
||||
//map from Arabian characters to their contextual form in the beginning of the word
|
||||
private static Map<Character, Character> contextualArabicIsolated = new HashMap<Character, Character>(){
|
||||
{
|
||||
put('ا', '\uFE8D');
|
||||
put('ب', '\uFE8F');
|
||||
put('ت', '\uFE95');
|
||||
put('ث', '\uFE99');
|
||||
put('ج', '\uFE9D');
|
||||
put('ح', '\uFEA1');
|
||||
put('خ', '\uFEA5');
|
||||
put('د', '\uFEA9');
|
||||
put('ذ', '\uFEAB');
|
||||
put('ر', '\uFEAD');
|
||||
put('ز', '\uFEAF');
|
||||
put('س', '\uFEB1');
|
||||
put('ش', '\uFEB5');
|
||||
put('ص', '\uFEB9');
|
||||
put('ض', '\uFEBD');
|
||||
put('ط', '\uFEC1');
|
||||
put('ظ', '\uFEC5');
|
||||
put('ع', '\uFEC9');
|
||||
put('غ', '\uFECD');
|
||||
put('ف', '\uFED1');
|
||||
put('ق', '\uFED5');
|
||||
put('ك', '\uFED9');
|
||||
put('ل', '\uFEDD');
|
||||
put('م', '\uFEE1');
|
||||
put('ن', '\uFEE5');
|
||||
put('ه', '\uFEE9');
|
||||
put('و', '\uFEED');
|
||||
put('ي', '\uFEF1');
|
||||
put('آ', '\uFE81');
|
||||
put('ة', '\uFE93');
|
||||
put('ى', '\uFEEF');
|
||||
put('ئ', '\uFE89');
|
||||
put('إ', '\uFE87');
|
||||
put('أ', '\uFE83');
|
||||
put('ء', '\uFE80');
|
||||
put('ؤ', '\uFE85');
|
||||
put((char)('ل' + 'آ'), '\uFEF5');
|
||||
put((char)('ل' + 'أ'), '\uFEF7');
|
||||
put((char)('ل' + 'إ'), '\uFEF9');
|
||||
put((char)('ل' + 'ا'), '\uFEFB');
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
//map from Arabian characters to their contextual form in the beginning of the word
|
||||
private static Map<Character, Character> contextualArabicBeginning = new HashMap<Character, Character>(){
|
||||
{
|
||||
put('ب', '\uFE91');
|
||||
put('ت', '\uFE97');
|
||||
put('ث', '\uFE9B');
|
||||
put('ج', '\uFE9F');
|
||||
put('ح', '\uFEA3');
|
||||
put('خ', '\uFEA7');
|
||||
put('س', '\uFEB3');
|
||||
put('ش', '\uFEB7');
|
||||
put('ص', '\uFEBB');
|
||||
put('ض', '\uFEBF');
|
||||
put('ط', '\uFEC3');
|
||||
put('ظ', '\uFEC7');
|
||||
put('ع', '\uFECB');
|
||||
put('غ', '\uFECF');
|
||||
put('ف', '\uFED3');
|
||||
put('ق', '\uFED7');
|
||||
put('ك', '\uFEDB');
|
||||
put('ل', '\uFEDF');
|
||||
put('م', '\uFEE3');
|
||||
put('ن', '\uFEE7');
|
||||
put('ه', '\uFEEB');
|
||||
put('ي', '\uFEF3');
|
||||
put('ئ', '\uFE8B');
|
||||
}
|
||||
};
|
||||
|
||||
//map from Arabian characters to their contextual form in the middle of the word
|
||||
private static Map<Character, Character> contextualArabicMiddle = new HashMap<Character, Character>(){
|
||||
{
|
||||
put('ب', '\uFE92');
|
||||
put('ت', '\uFE98');
|
||||
put('ث', '\uFE9C');
|
||||
put('ج', '\uFEA0');
|
||||
put('ح', '\uFEA4');
|
||||
put('خ', '\uFEA8');
|
||||
put('س', '\uFEB4');
|
||||
put('ش', '\uFEB8');
|
||||
put('ص', '\uFEBC');
|
||||
put('ض', '\uFEC0');
|
||||
put('ط', '\uFEC4');
|
||||
put('ظ', '\uFEC8');
|
||||
put('ع', '\uFECC');
|
||||
put('غ', '\uFED0');
|
||||
put('ف', '\uFED4');
|
||||
put('ق', '\uFED8');
|
||||
put('ك', '\uFEDC');
|
||||
put('ل', '\uFEE0');
|
||||
put('م', '\uFEE4');
|
||||
put('ن', '\uFEE8');
|
||||
put('ه', '\uFEEC');
|
||||
put('ي', '\uFEF4');
|
||||
put('ئ', '\uFE8C');
|
||||
}
|
||||
};
|
||||
|
||||
//map from Arabian characters to their contextual form in the end of the word
|
||||
private static Map<Character, Character> contextualArabicEnd = new HashMap<Character, Character>(){
|
||||
{
|
||||
put('ا', '\uFE8E');
|
||||
put('ب', '\uFE90');
|
||||
put('ت', '\uFE96');
|
||||
put('ث', '\uFE9A');
|
||||
put('ج', '\uFE9E');
|
||||
put('ح', '\uFEA2');
|
||||
put('خ', '\uFEA6');
|
||||
put('د', '\uFEAA');
|
||||
put('ذ', '\uFEAC');
|
||||
put('ر', '\uFEAE');
|
||||
put('ز', '\uFEB0');
|
||||
put('س', '\uFEB2');
|
||||
put('ش', '\uFEB6');
|
||||
put('ص', '\uFEBA');
|
||||
put('ض', '\uFEBE');
|
||||
put('ط', '\uFEC2');
|
||||
put('ظ', '\uFEC6');
|
||||
put('ع', '\uFECA');
|
||||
put('غ', '\uFECE');
|
||||
put('ف', '\uFED2');
|
||||
put('ق', '\uFED6');
|
||||
put('ك', '\uFEDA');
|
||||
put('ل', '\uFEDE');
|
||||
put('م', '\uFEE2');
|
||||
put('ن', '\uFEE6');
|
||||
put('ه', '\uFEEA');
|
||||
put('و', '\uFEEE');
|
||||
put('ي', '\uFEF2');
|
||||
put('آ', '\uFE82');
|
||||
put('ة', '\uFE94');
|
||||
put('ى', '\uFEF0');
|
||||
put('ئ', '\uFE8A');
|
||||
put('إ', '\uFE88');
|
||||
put('أ', '\uFE84');
|
||||
put('ؤ', '\uFE86');
|
||||
put((char)('ل' + 'آ'), '\uFEF6');
|
||||
put((char)('ل' + 'أ'), '\uFEF8');
|
||||
put((char)('ل' + 'إ'), '\uFEFA');
|
||||
put((char)('ل' + 'ا'), '\uFEFC');
|
||||
}
|
||||
};
|
||||
public enum contextualState{
|
||||
isolate,
|
||||
begin,
|
||||
middle,
|
||||
end
|
||||
}
|
||||
|
||||
public static boolean exceptionAfterLam(Character c){
|
||||
switch (c){
|
||||
case '\u0622':
|
||||
case '\u0623':
|
||||
case '\u0625':
|
||||
case '\u0627':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function return the contextual form of Arabic characters in a given state
|
||||
* @param c - the character to convert
|
||||
* @param state - the character state: beginning, middle, end or isolated
|
||||
* @return the contextual character
|
||||
*/
|
||||
public static Character getContextualSymbol(Character c, contextualState state) {
|
||||
Character newChar;
|
||||
switch (state){
|
||||
case begin:
|
||||
newChar = contextualArabicBeginning.get(c);
|
||||
break;
|
||||
case middle:
|
||||
newChar = contextualArabicMiddle.get(c);
|
||||
break;
|
||||
case end:
|
||||
newChar = contextualArabicEnd.get(c);
|
||||
break;
|
||||
case isolate:
|
||||
default:
|
||||
newChar = contextualArabicIsolated.get(c);;
|
||||
}
|
||||
if (newChar != null){
|
||||
return newChar;
|
||||
} else{
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function return the contextual state of a given character, depend of the previous
|
||||
* character state and the next charachter.
|
||||
* @param prevState - previous character state or isolated if none
|
||||
* @param curChar - the current character
|
||||
* @param nextChar - the next character or null if none
|
||||
* @return the current character contextual state
|
||||
*/
|
||||
public static contextualState getCharContextualState(contextualState prevState, Character curChar, Character nextChar) {
|
||||
contextualState curState;
|
||||
if ((prevState == contextualState.isolate || prevState == contextualState.end) &&
|
||||
contextualArabicBeginning.containsKey(curChar) &&
|
||||
contextualArabicEnd.containsKey(nextChar)){
|
||||
|
||||
curState = contextualState.begin;
|
||||
|
||||
} else if ((prevState == contextualState.begin || prevState == contextualState.middle) &&
|
||||
contextualArabicEnd.containsKey(curChar)){
|
||||
|
||||
if (contextualArabicMiddle.containsKey(curChar) && contextualArabicEnd.containsKey(nextChar)){
|
||||
curState = contextualState.middle;
|
||||
}else{
|
||||
curState = contextualState.end;
|
||||
}
|
||||
}else{
|
||||
curState = contextualState.isolate;
|
||||
}
|
||||
return curState;
|
||||
}
|
||||
|
||||
/**
|
||||
* this function convert given string to it's contextual form
|
||||
* @param s - the given string
|
||||
* @return the contextual form of the string
|
||||
*/
|
||||
public static String convertToContextual(String s){
|
||||
if (s == null || s.isEmpty() || s.length() == 1){
|
||||
return s;
|
||||
}
|
||||
|
||||
int length = s.length();
|
||||
StringBuilder newWord = new StringBuilder(length);
|
||||
|
||||
Character curChar, nextChar = s.charAt(0);
|
||||
contextualState prevState = contextualState.isolate;
|
||||
contextualState curState = contextualState.isolate;
|
||||
|
||||
for (int i = 0; i < length - 1; i++){
|
||||
curChar = nextChar;
|
||||
nextChar = s.charAt(i + 1);
|
||||
|
||||
if (curChar == 'ل' && exceptionAfterLam(nextChar)){
|
||||
i++;
|
||||
curChar = (char)(nextChar + curChar);
|
||||
if (i < length - 1) {
|
||||
nextChar = s.charAt(i + 1);
|
||||
}else{
|
||||
nextChar = curChar;
|
||||
prevState = curState;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
curState = getCharContextualState(prevState, curChar, nextChar);
|
||||
newWord.append(getContextualSymbol(curChar, curState));
|
||||
prevState = curState;
|
||||
|
||||
|
||||
}
|
||||
curState = getCharContextualState(prevState, nextChar, null);
|
||||
newWord.append(getContextualSymbol(nextChar, curState));
|
||||
|
||||
return newWord.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The function get a string and reverse it.
|
||||
* in case of end-of-word sign, it will leave it at the end.
|
||||
* in case of sign with direction like brackets, it will change the direction.
|
||||
* @param s - the string to reverse
|
||||
* @return reversed string
|
||||
*/
|
||||
public static String reverse(String s) {
|
||||
int j = s.length();
|
||||
int isEndLine = 0;
|
||||
char[] newWord = new char[j];
|
||||
|
||||
if (j == 0) {
|
||||
return s;
|
||||
}
|
||||
|
||||
for (int i = 0; i < s.length() - isEndLine; i++) {
|
||||
if (directionSignsMap.containsKey(s.charAt(i))) {
|
||||
newWord[--j] = directionSignsMap.get(s.charAt(i));
|
||||
} else {
|
||||
newWord[--j] = s.charAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
return new String(newWord);
|
||||
}
|
||||
|
||||
public static String fixWhitespace(String s){
|
||||
int length = s.length();
|
||||
|
||||
if (length > 0 && isSpaceSign(s.charAt(length - 1))){
|
||||
return s.charAt(length - 1) + s.substring(0, length - 1);
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The function get a string and fix the rtl words.
|
||||
* since simple reverse puts the beginning of the text at the end, the text should have been from bottom to top.
|
||||
* To avoid that, we save the text in lines (line max size can be change in the settings)
|
||||
* @param s - the string to fix.
|
||||
* @return a fix string.
|
||||
*/
|
||||
public static String fixRtl(String s) {
|
||||
if (s == null || s.isEmpty()){
|
||||
return s;
|
||||
}
|
||||
Log.d("ROIGR", "before: |" + org.apache.commons.lang3.StringEscapeUtils.escapeJava(s) + "|");
|
||||
|
||||
int length = s.length();
|
||||
String oldString = s.substring(0, length);
|
||||
String newString = "";
|
||||
List<String> lines = new ArrayList<>();
|
||||
char[] newWord = new char[length];
|
||||
int line_max_size = GBApplication.getPrefs().getInt("rtl_max_line_length", 18);
|
||||
|
||||
int startPos = 0;
|
||||
int endPos = 0;
|
||||
characterType CurRtlType = isRtl(oldString.charAt(0))? characterType.rtl : characterType.ltr;
|
||||
characterType PhraseRtlType = CurRtlType;
|
||||
|
||||
Character c;
|
||||
// String word = "", phrase = "", line = "";
|
||||
StringBuilder word = new StringBuilder();
|
||||
StringBuilder phrase = new StringBuilder();
|
||||
StringBuilder line = new StringBuilder();
|
||||
String phraseString = "";
|
||||
boolean addCharToWord = false;
|
||||
for (int i = 0; i < length; i++) {
|
||||
c = oldString.charAt(i);
|
||||
addCharToWord = false;
|
||||
Log.d("ROIGR", "char: " + c + " :" + Character.getDirectionality(c));
|
||||
// Log.d("ROIGR", "hex : " + (int)c);
|
||||
|
||||
if (isLtr(c)){
|
||||
CurRtlType = characterType.ltr;
|
||||
} else if (isRtl(c)) {
|
||||
CurRtlType = characterType.rtl;
|
||||
}
|
||||
|
||||
if ((CurRtlType == PhraseRtlType) && !(isSpaceSign(c) || isEndLineSign(c))){
|
||||
Log.d("ROIGR", "add: " + c + " to: " + word);
|
||||
word.append(c);
|
||||
addCharToWord = true;
|
||||
if (i < length - 1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
do {
|
||||
if (line.length() + phrase.length() + word.length() < line_max_size) {
|
||||
if (isSpaceSign(c)) {
|
||||
word.append(c);
|
||||
addCharToWord = true;
|
||||
}
|
||||
|
||||
phrase.append(word);
|
||||
word.setLength(0);
|
||||
|
||||
if (isSpaceSign(c)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
phraseString = phrase.toString();
|
||||
Log.d("ROIGR", "phrase: |" + phraseString + "|");
|
||||
if (PhraseRtlType == characterType.rtl) {
|
||||
if (contextualSupport()) {
|
||||
phraseString = convertToContextual(phraseString);
|
||||
}
|
||||
phraseString = reverse(phraseString);
|
||||
}
|
||||
|
||||
line.insert(0, fixWhitespace(phraseString));
|
||||
Log.d("ROIGR", "line now: |" + line + "|");
|
||||
phrase.setLength(0);
|
||||
|
||||
if (word.length() > 0){
|
||||
line.append('\n');
|
||||
} else if (isEndLineSign(c)) {
|
||||
line.append(c);
|
||||
} else if (!addCharToWord) {
|
||||
word.append(c);
|
||||
if (i == length - 1){
|
||||
addCharToWord = true;
|
||||
continue;
|
||||
}
|
||||
PhraseRtlType = PhraseRtlType == characterType.rtl ? characterType.ltr : characterType.rtl;
|
||||
break;
|
||||
}
|
||||
|
||||
lines.add(line.toString());
|
||||
Log.d("ROIGR", "line: |" + line + "|");
|
||||
line.setLength(0);
|
||||
|
||||
if (word.length() == 0){
|
||||
break;
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
}
|
||||
|
||||
lines.add(line.toString());
|
||||
|
||||
newString = TextUtils.join("", lines);
|
||||
|
||||
Log.d("ROIGR", "after : |" + org.apache.commons.lang3.StringEscapeUtils.escapeJava(newString) + "|");
|
||||
|
||||
return newString;
|
||||
}
|
||||
}
|
@ -20,6 +20,8 @@ import android.support.annotation.NonNull;
|
||||
|
||||
public class StringUtils {
|
||||
|
||||
|
||||
|
||||
@NonNull
|
||||
public static String truncate(String s, int maxLength){
|
||||
if (s == null) {
|
||||
|
@ -106,6 +106,11 @@
|
||||
<string name="pref_title_transliteration">Transliteration</string>
|
||||
<string name="pref_summary_transliteration">Enable this if your device has no support for your language\'s font</string>
|
||||
|
||||
<string name="pref_title_rtl">Right-To-Left</string>
|
||||
<string name="pref_summary_rtl">Enable this if your device has no support in right-to-left languages</string>
|
||||
<string name="pref_rtl_max_line_length">Right-To-Left Max Line Length</string>
|
||||
<string name="pref_rtl_max_line_length_summary">When Right-To-Left is enable, the text is separated to lines. Change this value if the lines are to long/short.</string>
|
||||
|
||||
<string name="always">Always</string>
|
||||
<string name="when_screen_off">When screen is off</string>
|
||||
<string name="never">Never</string>
|
||||
@ -641,6 +646,9 @@
|
||||
<string name="watch9_calibration_button">Calibrate</string>
|
||||
<string name="title_activity_watch9_pairing">Watch 9 pairing</string>
|
||||
<string name="title_activity_watch9_calibration">Watch 9 calibration</string>
|
||||
<string name="pref_title_contextual_arabic">Contextual Arabic</string>
|
||||
<string name="pref_summary_contextual_arabic">Enable this to support contextual Arabic</string>
|
||||
<string name="preferences_rtl_settings">Right To Left Support</string>
|
||||
<string name="share_log">Share log</string>
|
||||
<string name="share_log_warning">Please keep in mind Gadgetbridge log files may contain lots of personal information, including but not limited to health data, unique identifiers (such as a device MAC address), music preferences, etc. Consider editing the file and removing this information before sending the file to a public issue report.</string>
|
||||
<string name="warning">Warning!</string>
|
||||
|
@ -188,7 +188,32 @@
|
||||
android:summary="@string/pref_summary_transliteration"
|
||||
android:title="@string/pref_title_transliteration"
|
||||
/>
|
||||
<PreferenceScreen
|
||||
android:title="@string/preferences_rtl_settings">
|
||||
<CheckBoxPreference
|
||||
android:layout="@layout/preference_checkbox"
|
||||
android:defaultValue="false"
|
||||
android:key="rtl"
|
||||
android:summary="@string/pref_summary_rtl"
|
||||
android:title="@string/pref_title_rtl"
|
||||
/>
|
||||
|
||||
<CheckBoxPreference
|
||||
android:layout="@layout/preference_checkbox"
|
||||
android:defaultValue="false"
|
||||
android:key="contextualArabic"
|
||||
android:summary="@string/pref_summary_contextual_arabic"
|
||||
android:title="@string/pref_title_contextual_arabic"
|
||||
/>
|
||||
|
||||
<EditTextPreference
|
||||
android:inputType="number"
|
||||
android:key="rtl_max_line_length"
|
||||
android:defaultValue="20"
|
||||
android:maxLength="159"
|
||||
android:title="@string/pref_rtl_max_line_length"
|
||||
android:summary="@string/pref_rtl_max_line_length_summary"/>
|
||||
</PreferenceScreen>
|
||||
<CheckBoxPreference
|
||||
android:layout="@layout/preference_checkbox"
|
||||
android:defaultValue="false"
|
||||
@ -643,7 +668,7 @@
|
||||
android:title="@string/pref_title_screentime"/>
|
||||
|
||||
<CheckBoxPreference
|
||||
android:layout="@layout/preference_checkbox"
|
||||
android:layout="@layout/preference_checkbox"
|
||||
android:defaultValue="true"
|
||||
android:key="hplus_alldayhr"
|
||||
android:title="@string/prefs_title_all_day_heart_rate" />
|
||||
|
@ -114,4 +114,18 @@ public class DeviceCommunicationServiceTestCase extends TestBase {
|
||||
|
||||
assertEquals("Transliteration support fail!", "Prosto tekct", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRtlSupport() {
|
||||
SharedPreferences settings = GBApplication.getPrefs().getPreferences();
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putBoolean("rtl", true);
|
||||
editor.commit();
|
||||
|
||||
Intent intent = mDeviceService.createIntent().putExtra(EXTRA_NOTIFICATION_BODY, "English and עברית");
|
||||
mDeviceService.invokeService(intent);
|
||||
String result = intent.getStringExtra(EXTRA_NOTIFICATION_BODY);
|
||||
|
||||
assertEquals("Rtl support fail!", "תירבע English and", result);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,368 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.test;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.RtlUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests RtlUtils
|
||||
*/
|
||||
public class RtlUtilsTest extends TestBase {
|
||||
|
||||
// @Test
|
||||
private Character hebrew1 = 'א';
|
||||
private Character hebrew2 = 'ם';
|
||||
|
||||
private Character arabic1 = 'ا';
|
||||
private Character arabic2 = 'ن';
|
||||
|
||||
private Character english1 = 'a';
|
||||
private Character english2 = 'N';
|
||||
|
||||
private Character punctuation1 = '.';
|
||||
private Character punctuation2 = '-';
|
||||
|
||||
private Character space = ' ';
|
||||
|
||||
private Character endLine1 = '\0';
|
||||
private Character endLine2 = '\n';
|
||||
|
||||
private Character[] contextualIsolated = {'ء', '\uFE80'};
|
||||
private Character[] contextualBeginning = {'س', '\uFEB3'};
|
||||
private Character[] contextualMiddle = {'ض', '\uFEC0'};
|
||||
private Character[] contextualEndNoMiddle = {'آ', '\uFE82'};
|
||||
private Character[] contextualLam = {(char)('ل' + 'أ'), '\uFEF8'};
|
||||
|
||||
private String hebrewWord1 = "שלום";
|
||||
private String hebrewWord2 = "וברכה";
|
||||
private String hebrewPhrase = hebrewWord1 + " " + hebrewWord2;
|
||||
|
||||
private String arabicWord1 = "سلام";
|
||||
private String arabicWord2 = "البركة";
|
||||
private String arabicPhrase = arabicWord1 + " " + arabicWord2;
|
||||
|
||||
private String englishWord1 = "Hello";
|
||||
private String englishWord2 = "Welcome";
|
||||
private String englishPhrase = englishWord1 + " and " + englishWord2;
|
||||
|
||||
@Test
|
||||
public void testGetCharacterType() throws Exception {
|
||||
|
||||
RtlUtils.characterType actual = RtlUtils.getCharacterType(hebrew1);
|
||||
RtlUtils.characterType expected = RtlUtils.characterType.rtl;
|
||||
assertEquals("Get Hebrew Character type failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.getCharacterType(arabic1);
|
||||
expected = RtlUtils.characterType.rtl_arabic;
|
||||
assertEquals("Get Arabic Character type failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.getCharacterType(english1);
|
||||
expected = RtlUtils.characterType.ltr;
|
||||
assertEquals("Get Hebrew Character type failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.getCharacterType(' ');
|
||||
expected = RtlUtils.characterType.space;
|
||||
assertEquals("Get Hebrew Character type failed", expected, actual);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIsHebrew() throws Exception {
|
||||
boolean result = RtlUtils.isHebrew(hebrew1);
|
||||
assertTrue("Is Hebrew Character failed", result);
|
||||
|
||||
result = RtlUtils.isHebrew(hebrew2);
|
||||
assertTrue("Is Hebrew Character failed", result);
|
||||
|
||||
result = RtlUtils.isHebrew(arabic1);
|
||||
assertFalse("Is Hebrew Character failed", result);
|
||||
|
||||
result = RtlUtils.isHebrew(english1);
|
||||
assertFalse("Is Hebrew Character failed", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsArabic() throws Exception {
|
||||
boolean result = RtlUtils.isArabic(arabic1);
|
||||
assertTrue("Is Arabic Character failed", result);
|
||||
|
||||
result = RtlUtils.isArabic(arabic2);
|
||||
assertTrue("Is Arabic Character failed", result);
|
||||
|
||||
result = RtlUtils.isArabic(hebrew1);
|
||||
assertFalse("Is Arabic Character failed", result);
|
||||
|
||||
result = RtlUtils.isArabic(english1);
|
||||
assertFalse("Is Arabic Character failed", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsLtr() throws Exception {
|
||||
boolean result = RtlUtils.isLtr(english1);
|
||||
assertTrue("Is Ltr Character failed", result);
|
||||
|
||||
result = RtlUtils.isLtr(english1);
|
||||
assertTrue("Is Ltr Character failed", result);
|
||||
|
||||
result = RtlUtils.isLtr(hebrew1);
|
||||
assertFalse("Is Ltr Character failed", result);
|
||||
|
||||
result = RtlUtils.isLtr(arabic1);
|
||||
assertFalse("Is Ltr Character failed", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsRtl() throws Exception {
|
||||
boolean result = RtlUtils.isRtl(hebrew1);
|
||||
assertTrue("Is Rtl Character failed", result);
|
||||
|
||||
result = RtlUtils.isRtl(arabic1);
|
||||
assertTrue("Is Rtl Character failed", result);
|
||||
|
||||
result = RtlUtils.isRtl(english1);
|
||||
assertFalse("Is Rtl Character failed", result);
|
||||
|
||||
result = RtlUtils.isRtl(punctuation1);
|
||||
assertFalse("Is Rtl Character failed", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsPunctuation() throws Exception {
|
||||
boolean result = RtlUtils.isPunctuations(punctuation1);
|
||||
assertTrue("Is Punctuation Character failed", result);
|
||||
|
||||
result = RtlUtils.isPunctuations(punctuation1);
|
||||
assertTrue("Is Punctuation Character failed", result);
|
||||
|
||||
result = RtlUtils.isPunctuations(english1);
|
||||
assertFalse("Is Punctuation Character failed", result);
|
||||
|
||||
result = RtlUtils.isPunctuations(space);
|
||||
assertFalse("Is Punctuation Character failed", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsSpaceSign() throws Exception {
|
||||
boolean result = RtlUtils.isSpaceSign(space);
|
||||
assertTrue("Is Space Sign Character failed", result);
|
||||
|
||||
result = RtlUtils.isSpaceSign(punctuation1);
|
||||
assertFalse("Is Space Sign Character failed", result);
|
||||
|
||||
result = RtlUtils.isSpaceSign(english1);
|
||||
assertFalse("Is SpaceSign Character failed", result);
|
||||
|
||||
result = RtlUtils.isSpaceSign(hebrew1);
|
||||
assertFalse("Is SpaceSign Character failed", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEndLineSign() throws Exception {
|
||||
boolean result = RtlUtils.isEndLineSign(endLine1);
|
||||
assertTrue("Is End Line Sign Character failed", result);
|
||||
|
||||
result = RtlUtils.isEndLineSign(endLine2);
|
||||
assertTrue("Is End Line Sign Character failed", result);
|
||||
|
||||
result = RtlUtils.isEndLineSign(english1);
|
||||
assertFalse("Is End Line Sign Character failed", result);
|
||||
|
||||
result = RtlUtils.isEndLineSign(space);
|
||||
assertFalse("Is End Line Sign Character failed", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExceptionAfterLam() throws Exception {
|
||||
boolean result = RtlUtils.exceptionAfterLam('\u0623');
|
||||
assertTrue("Is Exception After Lam failed", result);
|
||||
|
||||
result = RtlUtils.exceptionAfterLam('\u0630');
|
||||
assertFalse("Is Exception After Lam failed", result);
|
||||
|
||||
result = RtlUtils.exceptionAfterLam('\u062A');
|
||||
assertFalse("Is Exception After Lam failed", result);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetContextualSymbol() throws Exception {
|
||||
|
||||
Character actual = RtlUtils.getContextualSymbol(contextualIsolated[0], RtlUtils.contextualState.isolate);
|
||||
Character expected = contextualIsolated[1];
|
||||
assertEquals("Get Contextual Symbol failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.getContextualSymbol(contextualBeginning[0], RtlUtils.contextualState.begin);
|
||||
expected = contextualBeginning[1];
|
||||
assertEquals("Get Contextual Symbol failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.getContextualSymbol(contextualMiddle[0], RtlUtils.contextualState.middle);
|
||||
expected = contextualMiddle[1];
|
||||
assertEquals("Get Contextual Symbol failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.getContextualSymbol(contextualEndNoMiddle[0], RtlUtils.contextualState.end);
|
||||
expected = contextualEndNoMiddle[1];
|
||||
assertEquals("Get Contextual Symbol failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.getContextualSymbol(contextualEndNoMiddle[0], RtlUtils.contextualState.middle);
|
||||
expected = contextualEndNoMiddle[0];
|
||||
assertEquals("Get Contextual Symbol failed", expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCharContextualState() throws Exception {
|
||||
|
||||
RtlUtils.contextualState actual = RtlUtils.getCharContextualState(RtlUtils.contextualState.isolate, contextualBeginning[0], contextualEndNoMiddle[0]);
|
||||
RtlUtils.contextualState expected = RtlUtils.contextualState.begin;
|
||||
assertEquals("Get Char Contextual State failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.getCharContextualState(RtlUtils.contextualState.begin, contextualMiddle[0], contextualEndNoMiddle[0]);
|
||||
expected = RtlUtils.contextualState.middle;
|
||||
assertEquals("Get Char Contextual State failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.getCharContextualState(RtlUtils.contextualState.begin, contextualMiddle[0], contextualIsolated[0]);
|
||||
expected = RtlUtils.contextualState.end;
|
||||
assertEquals("Get Char Contextual State failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.getCharContextualState(RtlUtils.contextualState.begin, contextualIsolated[0], contextualEndNoMiddle[0]);
|
||||
expected = RtlUtils.contextualState.isolate;
|
||||
assertEquals("Get Char Contextual State failed", expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertToContextual() throws Exception {
|
||||
|
||||
String actual = RtlUtils.convertToContextual("");
|
||||
String expected = "";
|
||||
assertEquals("Convert To Contextual failed", expected, actual);
|
||||
|
||||
char[] nonContextual = {'ج', 'ج', 'ج'};
|
||||
char[] contextual = {'\uFE9F', '\uFEA0', '\uFE9E'};
|
||||
actual = RtlUtils.convertToContextual(new StringBuilder().append(nonContextual).toString());
|
||||
expected = new StringBuilder().append(contextual).toString();
|
||||
assertEquals("Convert To Contextual failed", expected, actual);
|
||||
|
||||
char[] nonContextual2 = {'ج', 'ج', 'ل', '\u0622','ج'};
|
||||
char[] contextual2 = {'\uFE9F', '\uFEA0', '\uFEF6', '\uFE9D'};
|
||||
actual = RtlUtils.convertToContextual(new StringBuilder().append(nonContextual2).toString());
|
||||
expected = new StringBuilder().append(contextual2).toString();
|
||||
assertEquals("Convert To Contextual failed", expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReverse() throws Exception {
|
||||
|
||||
String actual = RtlUtils.reverse(hebrewWord1);
|
||||
String expected = "םולש";
|
||||
assertEquals("Reverse failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.reverse(hebrewPhrase);
|
||||
expected = "הכרבו םולש";
|
||||
assertEquals("Reverse failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.reverse("טקסט עם (סוגריים) וסימן שאלה?");
|
||||
expected = "?הלאש ןמיסו (םיירגוס) םע טסקט";
|
||||
assertEquals("Reverse failed", expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFixWhitespace() throws Exception {
|
||||
|
||||
String actual = RtlUtils.fixWhitespace(englishPhrase + " ");
|
||||
String expected = " " + englishPhrase;
|
||||
assertEquals("fix whitespace failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.fixWhitespace(englishPhrase);
|
||||
expected = englishPhrase;
|
||||
assertEquals("fix whitespace failed", expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFixRtl() throws Exception {
|
||||
|
||||
String actual = RtlUtils.fixRtl("Only english");
|
||||
String expected = "Only english";
|
||||
assertEquals("fix rtl failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.fixRtl("Only very long english with more than 18 characters");
|
||||
expected = " Only very long\n" +
|
||||
" english with more\n" +
|
||||
" than 18\n" +
|
||||
"characters";
|
||||
assertEquals("fix rtl failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.fixRtl("רק עברית");
|
||||
expected = "תירבע קר";
|
||||
assertEquals("fix rtl failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.fixRtl("English and עברית");
|
||||
expected = "תירבע English and";
|
||||
assertEquals("fix rtl failed", expected, actual);
|
||||
|
||||
actual = RtlUtils.fixRtl("משפט ארוך עם עברית and English וגם קצת סימנים כמו ?!$ (וגם ^.)");
|
||||
expected = " םע ךורא טפשמ\n" +
|
||||
" and English תירבע\n" +
|
||||
" םינמיס תצק םגו\n" +
|
||||
"(.^ םגו) $!? ומכ";
|
||||
assertEquals("fix rtl failed", expected, actual);
|
||||
|
||||
|
||||
|
||||
actual = RtlUtils.fixRtl("משפט עם כותרת\0ושמכיל גם ירידת\nשורה");
|
||||
expected = "תרתוכ םע טפשמ\0תדירי םג ליכמשו\n" +
|
||||
"הרוש";
|
||||
assertEquals("fix rtl failed", expected, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRtlSupport() throws Exception {
|
||||
setDefaultRtl();
|
||||
assertFalse("Rtl option fail! Expected 'Off' by default, but result is 'On'", RtlUtils.rtlSupport());
|
||||
|
||||
enableRtl(true);
|
||||
assertTrue("Rtl option fail! Expected 'On', but result is 'Off'", RtlUtils.rtlSupport());
|
||||
}
|
||||
|
||||
private void setDefaultRtl() {
|
||||
SharedPreferences settings = GBApplication.getPrefs().getPreferences();
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.remove("rtl");
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private void enableRtl(boolean enable) {
|
||||
SharedPreferences settings = GBApplication.getPrefs().getPreferences();
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putBoolean("rtl", enable);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContextualSupport() throws Exception {
|
||||
setDefaultContextual();
|
||||
assertFalse("Contextual option fail! Expected 'Off' by default, but result is 'On'", RtlUtils.contextualSupport());
|
||||
|
||||
enableContextual(true);
|
||||
assertTrue("Contextual option fail! Expected 'On', but result is 'Off'", RtlUtils.contextualSupport());
|
||||
}
|
||||
|
||||
private void setDefaultContextual() {
|
||||
SharedPreferences settings = GBApplication.getPrefs().getPreferences();
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.remove("contextualArabic");
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private void enableContextual(boolean enable) {
|
||||
SharedPreferences settings = GBApplication.getPrefs().getPreferences();
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putBoolean("contextualArabic", enable);
|
||||
editor.apply();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user