I have found a similar question, but I want something different.
I noticed that, when you don't have any connectivity, and you try to start Speech Recognition in an app, say through the RecognizerIntent class, you get a "no network connectivity - tap to view connectivity status" (see attached image), and if you click, a new Card with connectivity status appears.
How can I copy this behaviour in my own Activity, using the GDK?
EDIT: here is my code to call the Speech Recognition:
Intent speechIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
if (speechIntent.resolveActivity(getPackageManager()) != null) {
speechIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Talk to set your title:");
speechIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
Log.d(TAG, "Starting to record speech title");
startActivityForResult(speechIntent, Constants.SPEECH_TITLE_REQUEST_CODE);
}
Ok, I later found an answer to my problem. I'm using the MessageDialog class provided by pif
Here is how I am calling the card:
MessageDialog localDialog = new MessageDialog.Builder(context)
.setMessage("No network connectivity")
.setSecondaryMessage("Tap to view connection settings")
.setIcon(R.drawable.ic_cloud_sad_medium)
.setIsError(true)
.setDismissable(true)
.setAutoHide(false)
.setExpanded(true)
.setShowProgress(false)
.setIsManual(false)
.setListener(new MessageDialog.SimpleListener() {
public boolean onConfirmed() {
Log.d(TAG+"_noConnDialog", "onConfirm");
return true;
}
public void onDismissed() {
Log.d(TAG+"_noConnDialog", "onDismissed");
((Activity)context).finish();
}
public void onDone() {
Log.d(TAG+"_noConnDialog", "onDone");
Intent localIntent = new Intent(Settings.ACTION_WIFI_SETTINGS);
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
localIntent.putExtra("settings_id", 0);
context.startActivity(localIntent);
((Activity)context).finish();
}
}).build();
localDialog.show();
Notice that, as indicated in pif's disclaimer,
This is just for educational purposes and should not be used in any production apps until Google releases something similar officially.
I hope, Google will publish a set of Google Glass Views in the future releases of GDK. We really need them! Especially that awesome progress bar!
AFAIK the Glass Developer Team has not released these functionalities in the GDK yet.
Related
Just a quick question about the Google speech capture inbuilt on most Android devices (not the cloud service) - see the attached image. Is there any way, either programatically or via the settings, to control how long it waits until it asks you to try again? The 'complete silence' fields in the RecognizerIntent don't seem to make any difference. On certain devices it times out very quickly and the user doesn't have enough time to start speaking.
This is the code in my test app:
public void StartSpeechToText(ISpeechResultCallback callback)
{
string rec = global::Android.Content.PM.PackageManager.FeatureMicrophone;
if (rec == "android.hardware.microphone")
{
MainActivity activity = MainActivity.CurrentActivity;
activity.Callback = callback;
var voiceIntent = new Intent(RecognizerIntent.ActionRecognizeSpeech);
//var voiceIntent = new Intent(RecognizerIntent.ActionVoiceSearchHandsFree);
voiceIntent.PutExtra(RecognizerIntent.ExtraLanguageModel, RecognizerIntent.LanguageModelFreeForm);
voiceIntent.PutExtra(RecognizerIntent.ExtraPrompt, "Speak now");
voiceIntent.PutExtra(RecognizerIntent.ExtraSpeechInputCompleteSilenceLengthMillis, 1500);
voiceIntent.PutExtra(RecognizerIntent.ExtraSpeechInputPossiblyCompleteSilenceLengthMillis, 1500);
voiceIntent.PutExtra(RecognizerIntent.ExtraSpeechInputMinimumLengthMillis, 15000);
voiceIntent.PutExtra(RecognizerIntent.ExtraMaxResults, 1);
voiceIntent.PutExtra(RecognizerIntent.ExtraLanguage, Java.Util.Locale.Default);
activity.StartActivityForResult(voiceIntent, VOICE);
}
}
I am not sure about some documentation related stuff.
To sum up what I did and what I want to to: I managed to introduce voice recognition feature into an Android application that is running on Android 4.2 on a tablet, and it works ok. Now I want to port my application on Google Glass but unfortunately I get the following error when I try to start the speech recognizer: error 5 -> ERROR_CLIENT(Other client side errors). The message guides me to find other errors that not related to SpeechRecognizer object, but I don't get any in my logs, not even warnings. So my question would be: When exactly do I get ERROR_CLIENT? and what should look the errors that block the recognizer to start?
Thank you! :)
I found this link that is the source code for producing the errors.
SpeechRecognizer source
There are 7 places where a search found "ERROR_CLIENT"
Here's the log statements right before the ERROR_CLIENT is sent to onError
Log.e(TAG, "no selected voice recognition service");
Log.e(TAG, "bind to recognition service failed");
Log.e(TAG, "startListening() failed", e);
Log.e(TAG, "stopListening() failed", e);
Log.e(TAG, "cancel() failed", e);
Log.e(TAG, "not connected to the recognition service");
Of course you can find more info at the above link, but this should give you the general reasons why you'd get ERROR_CLIENT
So after a bit of pain I manage to solve my problem regarding my glass application.
First of all I found that SpeechRecognizer only works when my glasses are connected to the internet! Even so I still received from times to times ERROR 5. That was because I have a bad connectivity to the internet and from times to times my glass just disconnected from the internet without any notifications! I think this is an issue that must be solved for the next level of glasses. It just cannot disconnect from the internet without notifying you.
So one of the causes for ERROR_CLIENT(5) on Google Glass is: not having internet connection
I got this error because I was using an object variable inside the main loop:
class SpeechRecognition implements RecognitionListener {
private SpeechRecognizer recognizer;
public void transcribe (Activity activity) {
new Handler(Looper.getMainLooper()).post(() -> {
this.speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this.activity);
this.speechRecognizer.setRecognitionListener(this.listener);
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
this.speechRecognizer.startListening(intent);
}
}
[...]
}
Solution:
class SpeechRecognitionRunnable implements Runnable {
private volatile SpeechRecognizer speechRecognizer;
private RecognitionListener listener;
private Activity activity;
public SpeechRecognitionRunnable (RecognitionListener listener, Activity activity) {
this.listener = listener;
this.activity = activity;
}
#Override
public void run() {
this.speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this.activity);
this.speechRecognizer.setRecognitionListener(this.listener);
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
this.speechRecognizer.startListening(intent);
}
public SpeechRecognizer getSpeechRecognizer() {
return this.speechRecognizer;
}
}
class SpeechRecognition implements RecognitionListener {
private SpeechRecognizer recognizer;
public void transcribe (Activity activity) {
new Handler(Looper.getMainLooper()).post(new SpeechRecognitionRunnable(this, activity)
}
[...]
}
The error happens also if the Google search application has no permissions to the microphone. In that case, the phone speech recognition service will be disabled and the ERROR_CLIENT error will be triggered for all apps (the above case was verified on a Samsung phone running Android 11)
I have a code to check the TTS engine availability in the device. As the statement tts.getEngines().size() < 1 will be executed only from the API 14. I need to know if there is any way(methods) to check the same functionality even from the API 8.
Thanks in Advance
Code:
private void checkTTSAvailability() {
tts = new TextToSpeech(this, this);
ttsPresent=true;
**if (tts.getEngines().size() < 1) {**
PackageManager pm = getPackageManager();
Intent installIntent = new Intent();
installIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
ResolveInfo resolveInfo = pm.resolveActivity(installIntent, PackageManager.MATCH_DEFAULT_ONLY);
if (resolveInfo == null) {
Log.i("DragDrop", "No voice support on your phone");
Dialog d = new Dialog(this);
d.setTitle("Alert ");
TextView tv = new TextView(this);
tv.setText("No voice support on your phone");
d.setContentView(tv);
d.show();
ttsPresent=false;
// Not able to find the activity which should be started for
// this intent
} else {
Dialog d = new Dialog(this);
d.setTitle("Alert ");
TextView tv = new TextView(this);
tv.setText("Installing");
d.setContentView(tv);
d.show();
Log.i("DragDrop", "Installing");
startActivity(installIntent);
}
}
}
AFAIK, there is no way to list TTS engines or otherwise know they are installed, below API 14. Maybe you could review a list of all installed packages and recognize known TTS engine names there, but it's far from ideal.
The correct way of doing this would be to implement TextToSpeech.OnInitListener() callback in your activity, to know when the default system TTS engine was initialized, and if the initialization was a success or failure. Then, from within this callback on SUCCESS return, first set the language you need on the TTS object, then send an Intent to TTS to check for installed data (ACTION_CHECK_TTS_DATA) for that language, and again when the activity result comes, call ACTION_INSTALL_TTS_DATA if needed.
For a code example, see my other post, link below. It compares async operations in Android Java and C# for Android (with Xamarin for Android), but the Java code sample given there has the correctly functioning OnInitListener() callback.
How to implement Android callbacks in C# using async/await with Xamarin or Dot42?
Greg
I am developing a very simple app in here. It's for my Cerebral Palsy daughter. It's just a big YES and NO buttons, so she can press them when requested.
Well... I am using SVOX Classic TTS Engine.
Everything was running smoothly until my tablet upgraded to ICS. Now, everytime I run the app, it opens the Market asking for me to install TTS. I hit "back" and then, my app speaks. This is VERY annoying.
Here is what Google API says:
*A successful check will be marked by a CHECK_VOICE_DATA_PASS result code, indicating this device is ready to speak, after the creation of our TextToSpeech object. If not, we need to let the user know to install the data that's required for the device to become a multi-lingual talking machine! Downloading and installing the data is accomplished by firing off the ACTION_INSTALL_TTS_DATA intent, which will take the user to Android Market, and will let her/him initiate the download. Installation of the data will happen automatically once the download completes. Here is an example of what your implementation of onActivityResult() would look like:*
Here is my code:
public class yesOunoActivity extends Activity implements OnInitListener{
ImageView yes;
ImageView no;
public TextToSpeech tts;
private int MY_DATA_CHECK_CODE = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent checkIntent = new Intent();
checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
startActivityForResult(checkIntent, MY_DATA_CHECK_CODE);
tts = new TextToSpeech(this, this);
setContentView(R.layout.yesorno);
yes = (ImageView) findViewById(R.id.yes);
no = (ImageView) findViewById(R.id.no);
yes.setClickable(true);
yes.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent arg1) {
if (arg1.getAction() == android.view.MotionEvent.ACTION_DOWN) {
tts.speak("yes!", TextToSpeech.QUEUE_ADD, null);
}
return true;
}
});
no.setClickable(true);
no.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent arg1) {
if (arg1.getAction() == android.view.MotionEvent.ACTION_DOWN) {
//Intent myIntent = new Intent(v.getContext(), ParametrosActivity.class);
tts.speak("no!", TextToSpeech.QUEUE_ADD, null);
}
return true;
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == MY_DATA_CHECK_CODE) {
if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
// success, create the TTS instance
tts = new TextToSpeech(this, this);
} else {
// missing data, install it
//ATTENTION: BELOW THIS GIVES ME PROBLEMS SINCE IT OPENS MARKET
//AND I HAVE TO HIT THE BACK BUTTON, THEN, IT SPEAKS!
//BTW TTS ENGINE "IS" INSTALLED!!
Intent installIntent = new Intent();
installIntent
.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installIntent);
}
}
}
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
} else if (status == TextToSpeech.ERROR) {
}
}
#Override
public void onDestroy() {
if (tts != null) {
tts.stop();
tts.shutdown();
}
super.onDestroy();
System.gc();
}
}
If I remove the area with "ATTENTION" above (since I am SURE I have TTS installed), it works the first time I run the app, if I leave the app and I open it again, it says "speak failed: not bound to tts engine"
It's like it doesn't create the TTS object since the app is still in memory.
So, guys... what do you guys think that I should do??
This is driving me crazy and I really need to communicate to my daughter through the tablet!
Any help is appreciated!!
I had this trouble on my application as well: TTS works in 2.3, but when I tried 4.0, it had the same symptoms as your problem (which I just found now while searching for a solution). The engine would work if you force-closed the application through Settings and started it again but just "backing out" and going back made the TTS engine in ICS not bind.
I tried setting the TTS object (mTts) to null after running mTts.shutdown(). When I started the application again after backing out, I got a null error on my mTts.speak() line.
At least for ICS, something is not letting go of the TTS engine. My solution (for now) is that I have made my TTS object static:
// in Activity
private static TextToSpeech mTts;
.
.
.
// in onCreate()
mTts = new TextToSpeech(this, this);
.
.
.
// in onDestroy()
if (mTts != null) {
mTts.stop();
mTts.shutdown();
mTts = null;
}
I was already only using one TTS object for the application so I don't think there are too many downsides to this approach.
This is what I have in onActivityResult(...)
#Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
if (requestCode == MY_DATA_CHECK_CODE) {
/*
if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
// success, create the TTS instance
mTts = new TextToSpeech(this, this);
} else {
// missing data, install it
Intent installIntent = new Intent();
installIntent.setAction(
TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installIntent);
}
*/
if (mTts==null) {
Intent installIntent = new Intent();
installIntent.setAction(
TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installIntent);
}
mTts = new TextToSpeech(this, this);
}
}
Instead of using if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {...}, I check if mTts has already been instantiated. You'll also have to set mTts as static, as was mentioned by jlquant and Rakesh in an earlier post, so you'll have only a single instance of it. For example, private static TextToSpeech mTts.
So, unless you "Force stop" the app or it stops working because of an error, it won't anymore call the startActivity(installIntent);-- the annoying culprit that asks you to install TTS every time.
I fixed this by installing ivona having both Tts engines cured all problems, though I'm finding other issues such as no default tts is installed using the two together makes one work not a great fix but its s fix,
Also I can't find the option to allow my phone to install non market apps ie apk from my SD card
I had the same problem and solved. Maybe it's a timing issue in bounding, not sure, but a simple action before speak helped me.
Anyway I did this:
mTts = new TextToSpeech(this, this);
String engine = mTts.getDefaultEngine();
mTts = new TextToSpeech(this, this,engine);
Log.d("","...something here...");
Then when I hit my speak button, it speaks. You should watch your variable status on OnInit method. Maybe a separate thread can help talking in the app.
By the way, if you are sure TTS is installed, you can remove the block Intent checkIntent = new Intent(); for checking.
I definitely hope this helps you.
in my app I also used checkintent.setAction(...) to check if the necessary tts files are installed (which was working like a charm before upgrading to ICS). In ICS it always returned that the files are missing. So now I just ignore this check: i am creating the object and it initializes fine.
Moreover I was using two instances for two different languages. This also seems to not work anymore.
Now when I set the language for one of the instances, the other instance of the object is set to the same language (behaves like one instance).
gingerbread allowed setting of default tts in voice input& output / default tts
gingerbread only allows 'preferred' tts in language and input / text to speech output.
so tts is preferred instead of default, which just means it is not as clear as to which gets used.
The default with application overrides where stated is a better option I think.
If I create an app that depends on another app or apps (eg: the Facebook and Twitter apps), yet they are not installed, is there a method of checking for those dependencies and installing them at the same time as my own app?
I did this in my application which requires the zxing scanner app to be installed.
You will want this inside your onclick or ontouch:
try{
Intent intent = new Intent("com.google.zxing.client.android.SCAN");
intent.setPackage("com.google.zxing.client.android");
startActivityForResult(intent, 0);
} catch (Exception e) {
createAlert("Barcode Scanner not installed!", "This application uses " +
"the open source barcode scanner by ZXing Team, you need to install " +
"this before you can use this software!", true);
}
which calls
public void createAlert(String title, String message, Boolean button) {
// http://androidideasblog.blogspot.com/2010/02/how-to-add-messagebox-in-android.html
AlertDialog alertDialog;
alertDialog = new AlertDialog.Builder(this).create();
alertDialog.setTitle(title);
alertDialog.setMessage(message);
if ((button == true)) {
alertDialog.setButton("Download Now",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
Intent browserIntent = new Intent(
Intent.ACTION_VIEW,
Uri.parse("market://search?q=pname:com.google.zxing.client.android"));
startActivity(browserIntent);
}
});
}
alertDialog.show();
}
Then after sorting out all that code out I realise you asked for it to be installed at the same time as your app. Not sure if i should post this code, but it may be helpful
Short answer: No, you cannot automatically install other applications as dependencies.
Longer answer:
Android Market does not let you declare other applications to install as a dependency. As a system, Market appears to be designed for single application installs -- not Linux distro style mega dependency graphs.
At runtime, you can test for installed apps and punt your user over to the Market if so. See the techniques suggested by #QuickNick (testing if an app is installed) and #TerryProbert (punting to market) if that's what you want.
Your best bet is probably to design your app to gracefully degrade if dependencies are not available, and suggest (or insist) that they head over to market to install them.
Start from this:
Intent mediaIntent = new Intent("com.example.intent.action.NAME");
// add needed categories
List<ResolveInfo> listResolveInfo = getPackageManager().queryIntentServices(mediaIntent, 0);
if (listResolveInfo.size() != 0) {
//normal behavior
} else {
//install what you need
}
I give you example of querying services. If you want to check activities, then you will call queryIntentActivities().
I think following the pattern outlined in this post on the Android Developer Blog will help you.
http://android-developers.blogspot.com/2009/01/can-i-use-this-intent.html
As TerryProbert points out if you know that the Intent is not available prompt the user to install the missing app.
Here's what I use to return the first mission activity that exists:
try {
Class<?> missionClass = Class.forName(mPackageName+".Mission"+mission);
Method missionDescription;
missionDescription = missionClass.getMethod("missionDescription");
mMissionDescription = (String) missionDescription.invoke(null);
if (mMissionDescription.length() > 0) {
nextMission = mission;
break;
}
} catch (Exception e) {
//DEBUG*/Log.v(this.getClass().getName(), "onResume: Mission no "+mission+" not found: "+e.getMessage());
}
Each mission is held in a separate class, derived from a Mission base class. Derived classes are called Mission1, Mission24 etc.
Not all missions are defined.
The base class has an abstract class missionDescription which returns a string describing the mission.
This code is inside a loop so tests mission=1 to 99, trying to call missionDescription. It returns when the Description for the first mission found is returned.