Converting a character into keycode - android

I have a character and I want to convert it into KeyEvent KeyCode constraints http://developer.android.com/reference/android/view/KeyEvent.html#KEYCODE_0
Like if I have a character '0' I wan to convert into
Key code constant: '0' key.
Constant Value: 7 (0x00000007)
as specified in the KeyEvent page. What can be a best method for doing this? Is there any predefined function to do it?

Here is a solution I use to put chars in a webview:
char[] szRes = szStringText.toCharArray(); // Convert String to Char array
KeyCharacterMap CharMap;
if(Build.VERSION.SDK_INT >= 11) // My soft runs until API 5
CharMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
else
CharMap = KeyCharacterMap.load(KeyCharacterMap.ALPHA);
KeyEvent[] events = CharMap.getEvents(szRes);
for(int i=0; i<events.length; i++)
MainWebView.dispatchKeyEvent(events[i]); // MainWebView is webview

I'm still new to Java/Android, so my answer may not work out of the box, but you may still get the idea.
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
...
public class Sample {
...
public boolean convertStringToKeyCode(String text) {
KeyCharacterMap mKeyCharacterMap =
KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
KeyEvent[] events = mKeyCharacterMap.getEvents(text.toCharArray());
for (KeyEvent event2 : events) {
// We get key events for both UP and DOWN actions,
// so we may just need one.
if (event2.getAction() == 0) {
int keycode = event2.getKeyCode();
// Do some work
}
}
}
I got the idea when I was reading the code of sendText method in uiautomator framework source code:

Very rude solution but works for most characters.
Remember to do an uppercase if your text contains lowercase letters, you can add META_SHIFT_ON in that case if you then send a KeyEvent
for (int i = 0; i < text.length(); i++) {
final char ch = text.charAt(i);
try {
dispatch(KeyEvent.class.getField("KEYCODE_" + ch).getInt(null));
} catch (Exception e) {
Log.e(_TAG, "Unknown keycode for " + ch);
}
}

No, you cannot read a character "0" from the input and use a magical function to transform that to KeyEvent.KEYCODE_0 ... If you do, you will have to write a parser that switches on the read letter and return these values yourself.
For all I know, before reading the character you should've captured the thing in the onKey(). Depending on the number of keys you need to handle this way, a virtual android keyboard might be your only option, if this boilerplate code doesn't do the trick
switch(keyPress)
{
case '0': return KeyEvent.KEYCODE_0;
case '1': return ...
//...
case 'Z': return KeyEvent.KEYCODE_Z;
}

Related

Android - How to filter emoji (emoticons) from a string?

I'm working on an Android app, and I do not want people to use emoji in the input.
How can I remove emoji characters from a string?
Emojis can be found in the following ranges (source) :
U+2190 to U+21FF
U+2600 to U+26FF
U+2700 to U+27BF
U+3000 to U+303F
U+1F300 to U+1F64F
U+1F680 to U+1F6FF
You can use this line in your script to filter them all at once:
text.replace("/[\u2190-\u21FF]|[\u2600-\u26FF]|[\u2700-\u27BF]|[\u3000-\u303F]|[\u1F300-\u1F64F]|[\u1F680-\u1F6FF]/g", "");
Latest emoji data can be found here:
http://unicode.org/Public/emoji/
There is a folder named with emoji version.
As app developers a good idea is to use latest version available.
When You look inside a folder, You'll see text files in it.
You should check emoji-data.txt. It contains all standard emoji codes.
There are a lot of small symbol code ranges for emoji.
Best support will be to check all these in Your app.
Some people ask why there are 5 digit codes when we can only specify 4 after \u.
Well these are codes made from surrogate pairs. Usually 2 symbols are used to encode one emoji.
For example, we have a string.
String s = ...;
UTF-16 representation
byte[] utf16 = s.getBytes("UTF-16BE");
Iterate over UTF-16
for(int i = 0; i < utf16.length; i += 2) {
Get one char
char c = (char)((char)(utf16[i] & 0xff) << 8 | (char)(utf16[i + 1] & 0xff));
Now check for surrogate pairs. Emoji are located on the first plane, so check first part of pair in range 0xd800..0xd83f.
if(c >= 0xd800 && c <= 0xd83f) {
high = c;
continue;
}
For second part of surrogate pair range is 0xdc00..0xdfff. And we can now convert a pair to one 5 digit code.
else if(c >= 0xdc00 && c <= 0xdfff) {
low = c;
long unicode = (((long)high - 0xd800) * 0x400) + ((long)low - 0xdc00) + 0x10000;
}
All other symbols are not pairs so process them as is.
else {
long unicode = c;
}
Now use data from emoji-data.txt to check if it's emoji.
If it is, then skip it. If not then copy bytes to output byte array.
Finally byte array is converted to String by
String out = new String(outarray, Charset.forName("UTF-16BE"));
For those using Kotlin, Char.isSurrogate can help as well. Find and remove the indexes that are true from that.
Here is what I use to remove emojis. Note: This only works on API 24 and forwards
public String remove_Emojis_For_Devices_API_24_Onwards(String name)
{
// we will store all the non emoji characters in this array list
ArrayList<Character> nonEmoji = new ArrayList<>();
// this is where we will store the reasembled name
String newName = "";
//Character.UnicodeScript.of () was not added till API 24 so this is a 24 up solution
if (Build.VERSION.SDK_INT > 23) {
/* we are going to cycle through the word checking each character
to find its unicode script to compare it against known alphabets*/
for (int i = 0; i < name.length(); i++) {
// currently emojis don't have a devoted unicode script so they return UNKNOWN
if (!(Character.UnicodeScript.of(name.charAt(i)) + "").equals("UNKNOWN")) {
nonEmoji.add(name.charAt(i));//its not an emoji so we add it
}
}
// we then cycle through rebuilding the string
for (int i = 0; i < nonEmoji.size(); i++) {
newName += nonEmoji.get(i);
}
}
return newName;
}
so if we pass in a string:
remove_Emojis_For_Devices_API_24_Onwards("😊 test 😊 Indic:ढ Japanese:な 😊 Korean:ㅂ");
it returns: test Indic:ढ Japanese:な Korean:ㅂ
Emoji placement or count doesn't matter

How to make .contains search for every string in an array?

I have this code :
String[] whereyoufromarray = {"where", "you", "from"};
for (String whereyoufromstring : whereyoufromarray)
{
if (value.contains(whereyoufromstring)) {
//statement
}
}
But I want that if to only execute the statement if "value" has all of the words included in the array, something like "where are you from?". Currently if value has ONLY one of the words in the array the statement is executed.
I can do this with if (value.contains("where") && value.contains("you") && value.contains ("from")) but this just seems unnecessarily long. There has to be a workaround using arrays that I am missing.
Well, what is it?
p.s.: sorry for poor grammar. i'm suffering from sleep deprivation.
String[] whereyoufromarray = {"where", "you", "from"};
boolean valueContainsAllWordsInArray = true;
for (String whereyoufromstring : whereyoufromarray) {
// If one word wasn't found, the search is over, break the loop
if(!valueContainsAllWordsInArray) break;
valueContainsAllWordsInArray = valueContainsAllWordsInArray &&
value.contains(whereyoufromstring);
}
// valueContainsAllWordsInArray is now assigned to true only if value contains
// ALL strings in the array
For a case like this, I typically implement a function just to make the test. Let's call it containsAll()
public static boolean containsAll(String[] strings, String test)
{
for (String str : strings)
if (!test.contains(str))
return false;
return true;
}
And now you just do
if (containsAll(whereyoufromarray, value))
//statement
String[] whereyoufromarray = {"where", "you", "from"};
int arrayLength = whereyoufromarray.length;
int itemCount = 0;
for(String whereyoufromstring : whereyoufromarray)
{
if(value.contains(whereyoufromstring))
{
itemCount++;
}
}
if (itemCount == arrayLength){
//do your thing here
}
rough idea. I don't have my IDE up to proof this, but basically you can set a counter to = the length of your known array, then check each value in the array to see if it contains a match..if it does, increment another counter. At the end, test your counter to see if it matches the length of your array, so in your example, if itemCount= 3, then all values matched. if it was 2, then one would be missing and your method wouldn't execute.

Small Caps on TextViews, EditTexts, and Buttons in Android

Is there something I can do to make the text look in small caps/capital? As described here: http://en.wikipedia.org/wiki/Small_caps. I used a converter but some characters are missing.
EDIT 2015-08-02: As of API 21 (Lollipop) you can simply add:
android:fontFeatureSettings="smcp"
to your TextView declaration in XML, or at runtime, invoke:
textView.setFontFeatureSettings("smcp");
Of course, this only works for API 21 and up, so you'd still have to handle the old solution manually until you are only supporting Lollipop and above.
Being a bit of a typography geek at heart, this seemed like a really good question. I got to learn some more about Unicode today, as well as an answer for your question. :)
First, you'll need to have a font that includes "actual" small-caps characters. I'm assuming you know that since you're asking, but typically most professional fonts include these. Unfortunately most professional fonts are not licensed for distribution, so you may not be able to use them in your application. Anyway, in the event that you do find one (I used Chaparral Pro as an example here), this is how you can get small caps.
From this answer I found that the small caps characters (for A-Z) are located starting at Unicode-UF761. So I built a mapping of these characters:
private static char[] smallCaps = new char[]
{
'\uf761', //A
'\uf762',
'\uf763',
'\uf764',
'\uf765',
'\uf766',
'\uf767',
'\uf768',
'\uf769',
'\uf76A',
'\uf76B',
'\uf76C',
'\uf76D',
'\uf76E',
'\uf76F',
'\uf770',
'\uf771',
'\uf772',
'\uf773',
'\uf774',
'\uf775',
'\uf776',
'\uf777',
'\uf778',
'\uf779',
'\uf77A' //Z
};
Then added a helper method to convert an input string to one whose lowercase letters have been replaced by their Small Caps equivalents:
private static String getSmallCapsString (String input) {
char[] chars = input.toCharArray();
for(int i = 0; i < chars.length; i++) {
if(chars[i] >= 'a' && chars[i] <= 'z') {
chars[i] = smallCaps[chars[i] - 'a'];
}
}
return String.valueOf(chars);
}
Then just use that anywhere:
String regularCase = "The quick brown fox jumps over the lazy dog.";
textView.setText(getSmallCapsString(regularCase));
For which I got the following result:
Apologies for dragging up a very old question.
I liked #kcoppock's approach to this, but unfortunately the font I'm using is missing the small-cap characters. I suspect many others will find themselves in this situation.
That inspired me to write a little util method that will take a mixed-case string (e.g. Small Caps) and create a formatted spannable string that looks like Sᴍᴀʟʟ Cᴀᴘs but only uses the standard A-Z characters.
It works with any font that has the A-Z characters - nothing special required
It is easily useable in a TextView (or any other text-based view, for that matter)
It doesn't require any HTML
It doesn't require any editing of your original strings
I've posed the code here: https://gist.github.com/markormesher/3e912622d339af01d24e
Found an alternative here Is it possible to have multiple styles inside a TextView?
Basically you can use html tags formatting the size of the characters and give a small caps effect....
Just call this getSmallCaps(text) function:
public SpannableStringBuilder getSmallCaps(String text) {
text = text.toUpperCase();
text = text.trim();
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
if (text.contains(" ")) {
String[] arr = text.split(" ");
for (int i = 0; i < arr.length; i++) {
spannableStringBuilder.append(getSpannableStringSmallCaps(arr[i]));
spannableStringBuilder.append(" ");
}
} else {
spannableStringBuilder=getSpannableStringSmallCaps(text);
}
return spannableStringBuilder;
}
public SpannableStringBuilder getSpannableStringSmallCaps(String text) {
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(
text);
spannableStringBuilder.setSpan(new AbsoluteSizeSpan(36), 0, 1, 0);
spannableStringBuilder.setSpan(new StyleSpan(Typeface.BOLD), 0, 1, 0);
spannableStringBuilder.setSpan(new StyleSpan(Typeface.BOLD), 1,
text.length(), 0);
return spannableStringBuilder;
}
This is not my code but its works perfectly.
public SpannableString getSmallCapsString(String input) {
// values needed to record start/end points of blocks of lowercase letters
char[] chars = input.toCharArray();
int currentBlock = 0;
int[] blockStarts = new int[chars.length];
int[] blockEnds = new int[chars.length];
boolean blockOpen = false;
// record where blocks of lowercase letters start/end
for (int i = 0; i < chars.length; ++i) {
char c = chars[i];
if (c >= 'a' && c <= 'z') {
if (!blockOpen) {
blockOpen = true;
blockStarts[currentBlock] = i;
}
// replace with uppercase letters
chars[i] = (char) (c - 'a' + '\u0041');
} else {
if (blockOpen) {
blockOpen = false;
blockEnds[currentBlock] = i;
++currentBlock;
}
}
}
// add the string end, in case the last character is a lowercase letter
blockEnds[currentBlock] = chars.length;
// shrink the blocks found above
SpannableString output = new SpannableString(String.valueOf(chars));
for (int i = 0; i < Math.min(blockStarts.length, blockEnds.length); ++i) {
output.setSpan(new RelativeSizeSpan(0.8f), blockStarts[i], blockEnds[i], Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
}
return output;
}
Example:
SpannableString setStringObj = getSmallCapsStringTwo("Object"); tvObj.setText(setStringObj);
in XML
edit text has property :android:capitalize=""

Android Text To Speech And Numbers

I am using Android Text to Speech engine for reading some text and it's working. But my text contains numbers and I want the numbers to be read digit by digit.
I couldn't find anything in the documentation, but I am still hoping someone knows how I can do that.
The API does not allow you to specify how the text should be read so your code has to modify the text input so that it reads the individual numbers.
I suggest adding a space in between each number. That should cause the TextToSpeech to read the individual numbers.
If you need some code to help you detect numbers use this:
private boolean isNumber(String word)
{
boolean isNumber = false;
try
{
Integer.parseInt(word);
isNumber = true;
} catch (NumberFormatException e)
{
isNumber = false;
}
return isNumber;
}
The accepted answer has a minor flaw . If the number has '0' as one of it's digits , it would be read as alphabet 'o' instead of Zero . I would suggest the following solution .
String number = "1230";
for(int i = 0 ; i < number.size(); i++) {
/* refer Speech API , Don't use QUEUE_FLUSH as it results in flushing
some characters in this case */
engine.speak(Character.toString(number.charAt(i)),QUEUE_ADD,null);
}
/* refer Speech API , Don't use QUEUE_FLUSH as it results in flushing
some characters in this case */
for(int i = 0 ; i < number.size(); i++) {
engine.speak(Character.toString(number.charAt(i)),QUEUE_ADD,null);
}

Injecting non-English characters in Android

I am programming on a remote control app. One of the tasks is injecting characters. The code I am currently using looks like this:
Instrumentation instr = new Instrumentation();
String str="a";
// basically the same like calling instr.sendStringSync(str);
char[] chars = str.toCharArray();
KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
KeyEvent[] keyEvents = keyCharacterMap.getEvents(chars);
if (keyEvents != null) {
for (KeyEvent kev : keyEvents) {
instr.sendKeySync(kev);
}
}
That works perfectly on English characters (The characters show up in EditText boxes). However, if I am trying to inject e.g. Korean characters, this fails. The function getEvents returns null, even when I have configured Korean language and keyboard.
I know there is another method for injecting strings directly:
KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(), str, 0, 0);
instr.sendKeySync(event);
This is not working either - no characters shown in EditText boxes, and onKeyMultiple() is not called either in my test activity.
This is strange since dispatchKeyEvent() with the same event works in my test activity:
KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(), str, 0, 0);
dispatchKeyEvent(event);
My remote control app needs to inject events no matter to which activity. This is possible using Instrumentation (with android.permission.INJECT_EVENTS and a signature with the platform key).
How can I inject non-English characters using instrumentation?
Is there another way to accomplish this? E.g. Using dispatchKeyEvent (has to work for other activities/apps as well).
I leave the part above as extra info. I have found a solution. It requires to have root, but if you can sign with the application key I guess that's not a problem. What you can do is edit the file Virtual.kcm (/system/usr/keychars/Virtual.kcm), which is the default key character map (kcm). You can add any character you want, and then use the method Instrumentation.sendStringSync(String string), because it will be able to generate KeyEvents from the new kcm.
I had some problems editting the kcm on the phone, so what I did was to copy it on a computer, edit it there and then copy it back to the device.
I hope this helps!
In this link, the following content appears. It seams that the virtual keyboard has a US keyCharacterMap and layout, no matter what you choose on settings. I haven't been able to find a way to solve this.
Language Note
Android does not currently support multilingual keyboards. Moreover, the built-in generic key character map assumes a US English keyboard layout.
OEMs are encouraged to provide custom key character maps for their keyboards if they are designed for other languages.
Future versions of Android may provide better support for multilingual keyboards or user-selectable keyboard layouts.
You can use this method and InputFilter for EditText:
private boolean isLatinSymbolOrDigit(char c)
{
// Allow [a-zA-Z0-9]
if ('0' <= c && c <= '9')
return true;
if ('a' <= c && c <= 'z')
return true;
if ('A' <= c && c <= 'Z')
return true;
return false;
}
InputFilter filter = new InputFilter()
{
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend)
{
for (int i = start; i < end; i++)
{
if (isLatinSymbolOrDigit(source.charAt(i)))
{
}
else
{
//wrong character
return "";
}
}
return null;
}
};
editText.setFilters(new InputFilter[] {filter});

Categories

Resources