I'm building a custom Android TextToSpeechService which filters text by language.
Currently, I'm handling non-English-language text with my custom TextToSpeech service, and I'm sending English language text to an instance of Google Speech Services.
My problem is that, when I use Google Talkback, I'm no longer able to receive Talkback's usage hints (e.g. "Double-tap to activate", etc.)
The following code runs on a singleton which is called from my TextToSpeechService.
private TextToSpeech mAndroidTTS = null;
public void initEnglishTTS() {
if (mAndroidTTS == null) {
mAndroidTTS = new TextToSpeech(mContext, i -> {
if (i == TextToSpeech.SUCCESS) {
mAndroidTTS.setLanguage(Locale.US);
}
}, "com.google.android.tts");
}
}
public void synthesize(String text, SynthesisCallback callback) {
callback.start(SAMPLING_RATE_HZ,
AudioFormat.ENCODING_PCM_16BIT, 1);
sayInEnglish(text); // If I comment out this line, then it works
callback.done();
}
protected static void sayInEnglish(String text) {
String utteranceID = UUID.randomUUID().toString();
mAndroidTTS.speak(text, TextToSpeech.QUEUE_FLUSH, null, utteranceID);
}
In my custom TextToSpeechService, I have the following code:
public class CustomTtsService extends TextToSpeechService {
// an instance of the singleton
private SynthesizerSingleton singleton = singleton.getInstance();
#Override
protected void onSynthesizeText(SynthesisRequest request, SynthesisCallback callback) {
String text = getRequestString(request);
singleton.synthesize(text, callback);
}
private String getRequestString(SynthesisRequest request) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return request.getCharSequenceText().toString();
} else {
return request.getText();
}
}
}
If I were to comment out the line sayInEnglish(text), then all of the expected requests are received--but obviously, no utterances are synthesized.
What could I be doing incorrectly?
EDIT: The whole pipeline fails silently, neither the service nor the text string are null.
Related
Okay, so I am working on an app that will auto accept lyft request, but I am having a problem with my code not using performAction(AccessibilityNodeInfo.ACTION_CLICK); correctly.
public class AutoService extends AccessibilityService {
private static LyftAdapter lyftAdapter = new LyftAdapter();
// Automated Service (onAccessibilityEvent)
#TargetApi(16)
#Override
public void onAccessibilityEvent(AccessibilityEvent event)
{
AccessibilityNodeInfo source = event.getSource();
String lyftPackage = "com.lyft.android.driver";
String packageName = Tools.getPackage(source);
if (!packageName.equals(lyftPackage))
{
event.recycle();
return;
}
if (source == null)
{
event.recycle();
return;
}
processUI(event.getSource());
}
public void processUI(AccessibilityNodeInfo source)
{
source = getRootInActiveWindow();
if (Tools.getPackage(source).equals("com.lyft.android.driver") || Tools.getPackage(source).equals("me.lyft.android"))
{
if (!Lyft_Status.equals("OFFLINE"))
{
lyftAdapter.processEvent(source);
}
else
{
Log.v(TAG, "Can't process UI: " + Lyft_Status);
}
}
if (source != null)
source.recycle();
}
}
public abstract class RideshareAdapter {
public void processEvent(final AccessibilityNodeInfo source)
{
final StringBuilder sb = new StringBuilder();
processSubEvent(source, 0, sb);
final String string = sb.toString();
if (string == null)
{
Log.v(TAG, "String is NULL");
return;
}
processUIText(source, string.toLowerCase());
}
// PROCESS SECONDARY EVENT
private void processSubEvent(final AccessibilityNodeInfo source, final int n, final StringBuilder sb) {
for (int i = 0; i < n; ++i) {
sb.append("\t");
}
if (source != null)
{
sb.append(Tools.getText(source));
sb.append("\n");
final int childCount = source.getChildCount();
for (int j = 0; j < childCount; ++j) {
final AccessibilityNodeInfo child = source.getChild(j);
processSubEvent(child, n + 1, sb);
if (child != null) {
child.recycle();
}
}
}
}
// CLICK THE SCREEN
protected void clickScreen(AccessibilityNodeInfo source, final String text)
{
final AccessibilityNodeInfo s = source;
new Handler().postDelayed(new Runnable() {
List<AccessibilityNodeInfo> list = s.findAccessibilityNodeInfosByText(text);
#Override
public void run() {
for (final AccessibilityNodeInfo node : list) {
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
}, 1000);
}
}
public class LyftAdapter
extends RideshareAdapter
{
// LYFT ADAPTER
protected void processUIText(AccessibilityNodeInfo source, String text)
{
// RIDE REQUEST
if (text.contains("tap here to accept"))
{
clickScreen(source, "Tap here to accept");
{
}
The string comes out as (Just like it is shown):
Lyft
11 mins
away
Passenger Name
New
Tap here to accept
But for some reason, it triggers saying it is going to click on "Tap here to accept" textview, but it never actually does it. Any suggestions?
To be completely honest, your post is very difficult to read. You have functions that you have defined purely for organizational purposes and not because they are meant to be re-used. It makes it very difficult to parse and understand over the course of a StackOverflow post... Yet you did not provide enough for me to copy and paste and make sense of in Android Studio.
When you post code on StackOverflow you should go for a minimal replicating example and you ABSOLUTELY should remove your random Log calls. You may need them to help you understand what's happening, but hopefully WE do not :) and they just clutter things and make it more difficult to read your code. THIS BEING SAID, allow me to focus on one bit,
Note that I have cleaned up some of the poor style and debugging statements. Answers are in the code comments!
protected void clickScreen(final AccessibilityNodeInfo source, final String text)
{
new Handler().postDelayed(new Runnable() {
//Find ALL of the nodes that match the "text" argument.
List<AccessibilityNodeInfo> list = source.findAccessibilityNodeInfosByText(text);
#Override
public void run() {
//Non discrliminintly click them, whether they're buttons, or text fields or links... just click them and hope they do something.
for (final AccessibilityNodeInfo node : list) {
node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
//Delay it for a second AFTER the function has been called for no particularly good reason besides perhaps invalidating all of the nodes in the heirarchy... GOOD CALL!
}, 1000);
}
Given the above issues and the aforementioned generic code quality issues, it is difficult to provide a concise answer. This post leaves too many potential issues. Any provided answer would be a stab in the dark. I find it MOST likely that the problem is covered in my code comments, but it could most definitely be elsewhere. Also, my apologies for the sass!
All this being said, you might try this version of the function!
static void clickFirstMatchingNode(AccessibilityService service, final String text) {
final List<AccessibilityNodeInfo> list = service.getRootInActiveWindow().findAccessibilityNodeInfosByText(text);
for (AccessibilityNodeInfo node : list) {
//Check if the action completely successfully. Also, only click one of them. This is kind of an assumption, it also simplifies the logic. You can certainly write a version of this that clicks everything that matches!
if (node.performAction(AccessibilityNodeInfo.ACTION_CLICK)) return;
}
//If no node is successfully clicked Log some stuff!
Log.wtf(YourService.class.getName(), "Failed to click any nodes! WTF?: " + text);
}
NOTE: None of the above mentioned anything to do with your use of Accessibility APIs! I think that that is interesting.
I am facing a design problem. If I have (for example) on the left dockable view a list view that contains some pojo's, how do I notify the center dockable which one is selected? I am trying to implement some kind of Master-Detail-View where the user selects one item and then can configure it in the center area and the right area.
Thanks in advance :)
It depends on how you want to design your application.
If you want to create a separate Editor for each pojo then you can have a look at the LeftTestPane provided by the drombler fx archetype for a sample.
#FXML
private void onNewSampleAction(ActionEvent event) {
sampleCounter++;
Sample sample = new Sample("Sample " + sampleCounter);
SampleEditorPane sampleEditorPane = new SampleEditorPane(sample);
Dockables.inject(sampleEditorPane);
Dockables.open(sampleEditorPane);
}
There is currently no API for selecting an already opened editor, but please note that editors are currently being improved with the work done for issue #111.
If you want a single detail view then you can use the Context Framework, which allows components such as Dockables and Actions to communicate in a loosly coupled way.
The ListView should implement LocalContextProvider and keep the selected pojo in its local Context.
#ViewDocking(...)
public class ListView extends SomeNode implements LocalContextProvider {
private final SimpleContextContent contextContent = new SimpleContextContent();
private final SimpleContext context = new SimpleContext(contextContent);
private MyPojo currentSelection;
...
#Override
public Context getLocalContext() {
return context;
}
...
if (currentSelection != null){
contextContent.remove(currentSelection);
}
currentSelection = <current selection>
if (currentSelection != null){
contextContent.add(currentSelection);
}
...
}
In this case, the DetailsView should be registered as a view (singleton), too, and implement LocalContextProvider as well as ActiveContextSensitive:
#ViewDocking(...)
public class DetailsPane extends SomeNode implements ActiveContextSensitive, LocalContextProvider {
private final SimpleContextContent contextContent = new SimpleContextContent();
private final SimpleContext context = new SimpleContext(contextContent);
private Context activeContext;
private MyPojo myPojo;
...
#Override
public Context getLocalContext() {
return context;
}
#Override
public void setActiveContext(Context activeContext) {
this.activeContext = activeContext;
this.activeContext.addContextListener(MyPojo.class, (ContextEvent event) -> contextChanged());
contextChanged();
}
private void contextChanged() {
MyPojo newMyPojo = activeContext.find(MyPojo.class);
if ((myPojo == null && newMyPojo != null) || (myPojo null && !sample.equals(newMyPojo))) {
if (myPojo != null) {
unregister();
}
myPojo = newMyPojo;
if (myPojo != null) {
register();
}
}
}
private void unregister() {
contextContent.remove(myPojo);
//reset DetailsView
}
private void register() {
// configure DetailsView
contextContent.add(myPojo);
}
...
}
Have a look at the RightTestPane provided by the drombler fx archetype for a sample.
I am developing a multi player game, which have a module which works on the basis of notifications send by server. For example: Action of other player, score update, action to do etc.
I am receiving notification in json format. I am wondering if there is some codding pattern exist which automatically deliver different notifications to their corresponding handlers. Many thanks for your help.
Well, cannot say if this classifies as a pattern:
My take on it would be to simply create a separate class, lets call it JSONGameStateFilter, to filter the JSON object based on the received value plus the state of the game
Something like:
public class JSONGameStateFilter() {
public interface GameInterface1 {
// callback methods for activity 1
// example: public void newPlayerArrived(String name, int score);
// ...
}
public interface GameInterface2 {
// callback methods for activity 2
}
public interface GameInterface3 {
// callback methods for activity 3
}
private GameInterface1 callback1;
private GameInterface2 callback2;
private GameInterface3 callback3;
private JSONGameStateFilter instance;
public static JSONGameStateFilter getInstance() {
if (instance != null) {
return instance = new JSONGameStateFilter();
}
}
private JSONGameStateFilter() {}
public void registerListener(GameInterface1 callback) {
// called by Activity1 implementing GameInterface1
// by JSONGameStateFilter.newInstance().registerListener(this);
this.callback1 = callback;
}
public void registerListener(GameInterface2 callback) {
this.callback2 = callback;
}
public void registerListener(GameInterface3 callback) {
this.callback3 = callback;
}
public void filterJSON(JSONObject object) {
// read JSON and gamestate
// depending on situation call the right callback
// example: if (callback1 != null) callback1.newPlayerArrived(name, score)
}
}
The design of this approach would be to implement varies of callbacks on each activity (known pattern for fragments to communicate back to activity).
This is untested and written just now but I am pretty confident that it would work well.
I want to change the title of my page periodically, i.e. add a (*) in front of the current page title and remove it after a couple of seconds. I want to turn this title change on and off in code.
I get and set the page title from:
public static native void setPageTitle(String title) /*-{
$doc.title = title;
}-*/;
public static native String getPageTitle() /*-{
return $doc.title;
}-*/;
But how should I write a function that will change the page title every 300 miliseconds while adding and removing a prefix?
What I tried was:
private void changePageTitle(final String prefix) {
new Timer() {
#Override
public void run() {
String pageTitle =getPageTitle();
if (pageTitle.startsWith(prefix)) {
pageTitle = pageTitle.substring(prefix.length());
}
else {
pageTitle = pageTitle + prefix;
}
setPageTitle(pageTitle);
}
}
}.schedule(300);
}
This does not work. And I do not know how to switch the process on and off?
The Effect should be like in Facebook. When a new message arrive and you are not on the Facebook browser tab, then the tab shows a notification which is blinking.
You have to change schedule(300) by scheduleRepeating(300).
You should use just one instance of Timer or save the last timer to cancel it before creating a new one.
BTW: you dont need to write any JSNI to access the window title, just use Window.getTitle() and Window.setTitle(String)
EDITED:
This should work:
// create just an instance of the timer
final MyUpdateTitleTimer mytimer = new MyUpdateTitleTimer();
// To Start the updater
mytimer.setPrefix("> ");
// To Stop set the prefix to null
mytimer.setPrefix(null);
class MyUpdateTitleTimer extends Timer {
private String prefix;
private String title;
private boolean b;
public void run() {
String s = (b = !b) ? prefix + title : title;
Window.setTitle(s);
}
public void setPrefix(String prefix) {
if (title != null) {
Window.setTitle(title);
}
this.prefix = prefix;
if (prefix == null) {
cancel();
} else {
title = Window.getTitle();
scheduleRepeating(300);
}
}
}
I currently have code to share a variable between two entry points in my application. The variable is the iconCount variable used to indicate how many notices the user has which is displayed on the home screen beside the icon. The way I've managed to do this is with a singleton and it (seems) to work fine at the moment. The issue is now that I do not want those notices to reset to zero when I completely turn off and turn on the phone. Should there be 7 notifications, I want there to be 7 notifications even after a device restart. For this I apparently need a persistent store integration which I've researched for a while.
So far my code for the bare singleton is:
public class MyAppIndicator{
public ApplicationIndicator _indicator;
public static MyAppIndicator _instance;
MyAppIndicator () {
setupIndicator();
}
public static MyAppIndicator getInstance() {
if (_instance == null) {
_instance = new MyAppIndicator ();
}
return(_instance);
}
public void setupIndicator() {
//Setup notification
if (_indicator == null) {
ApplicationIndicatorRegistry reg = ApplicationIndicatorRegistry.getInstance();
_indicator = reg.getApplicationIndicator();
if(_indicator == null) {
ApplicationIcon icon = new ApplicationIcon(EncodedImage.getEncodedImageResource ("notificationsdemo_jde.png"));
_indicator = reg.register(icon, false, true);
_indicator.setValue(0);
_indicator.setVisible(false);
}
}
}
public void setVisible1(boolean visible, int count) {
if (_indicator != null) {
if (visible) {
_indicator.setVisible(true);
_indicator.setValue(count); //UserInterface.incrementCount()
} else {
_indicator.setVisible(false);
}
}
}
}
I have been using the blackberry tutorial to figure out how to implement the persistable storage: http://supportforums.blackberry.com/t5/Java-Development/Storing-persistent-data/ta-p/442747
Now before I go any further I must stress I'm very new to java development so my coding might be completely wrong, but here is what I've tried to do:
public void setVisible1(boolean visible, int count) {
if (_indicator != null) {
if (visible) {
_indicator.setVisible(true);
_indicator.setValue(count); //UserInterface.incrementCount()
StoreInfo info = new StoreInfo();
info.incElement();
synchronized (persistentCount) {
//persistentCount.setContents(_data);
persistentCount.commit();
}
} else {
_indicator.setVisible(false);
}
}
}
static {
persistentCount = PersistentStore.getPersistentObject(0xdec6a67096f833cL);
synchronized (persistentCount) {
if (persistentCount.getContents() == null) {
persistentCount.setContents(new Vector()); //don't know what to do with this?
persistentCount.commit();
}
}
}
private static final class StoreInfo implements Persistable{
private int iconCount;
public StoreInfo(){}
public int getElement(){
return (int)iconCount;
}
public void incElement(){
iconCount++; //persistently increment icon variable
}
public void resetElement(){
iconCount=0; //when user checks application
}
}
The code above doesn't work which I'd expect somehow because I'm having trouble implementing the persistent portion. If anyone has any idea or input on how to accomplish this any assistance would be helpful. And of course thanks in advance.
In the example they have a variable called _data that holds the StoreInfo class, so first of all you should be keeping the StoreInfo in some variable. To do this have something like the following in your static initializer:
persistentCount = PersistentStore.getPersistentObject(0xdec6a67096f833cL);
synchronized (persistentCount) {
if (persistentCount.getContents() == null) {
persistentCount.setContents(new StoreInfo());
persistentCount.commit();
}
}
_data = (StoreInfo)persistentCount.getContents();
Now when you want to update it and save to the PersistentStore you can have something like:
_data.incElement();
synchronized(persistentCount) {
persistentCount.setContents(_data);
persistentCount.commit();
}
Assuming you're going to only ever have one instance of StoreInfo it could be better to put the commit code into the modifier methods so you don't forget to save the new values to the PersistentStore.