Currently I am using
mAudioManager.registerMediaButtonEventReceiver(mRemoteControlResponder);
but this is now deprecated in 5.0 and replaced by setMediaButtonReceiver. There are 5 links in Google all pointing to developer.android.com.
Has anyone used this yet? If so can you provide an example?
Check this page: http://grepcode.com/file/repo1.maven.org/maven2/org.robolectric/android-all/5.0.0_r2-robolectric-0/android/media/session/MediaSession.java It is a rather large example of the complete flow.Here one of the most relevant parts
Set a pending intent for your media button receiver to allow restarting playback after the session has been stopped. If your app is started in this way an android.content.Intent.ACTION_MEDIA_BUTTON intent will be sent via the pending intent.
Parameters: nullmbr The android.app.PendingIntent to send the media button event to.
public void More ...setMediaButtonReceiver(#Nullable PendingIntent mbr) {
try {
mBinder.setMediaButtonReceiver(mbr);
} catch (RemoteException e) {
Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
}
}
Related
It looks as though Google has made offline speech recognition available from Google Now for third-party apps. It is being used by the app named Utter.
Has anyone seen any implementations of how to do simple voice commands with this offline speech rec? Do you just use the regular SpeechRecognizer API and it works automatically?
Google did quietly enable offline recognition in that Search update, but there is (as yet) no API or additional parameters available within the SpeechRecognizer class. {See Edit at the bottom of this post} The functionality is available with no additional coding, however the user’s device will need to be configured correctly for it to begin working and this is where the problem lies and I would imagine why a lot of developers assume they are ‘missing something’.
Also, Google have restricted certain Jelly Bean devices from using the offline recognition due to hardware constraints. Which devices this applies to is not documented, in fact, nothing is documented, so configuring the capabilities for the user has proved to be a matter of trial and error (for them). It works for some straight away – For those that it doesn't, this is the ‘guide’ I supply them with.
Make sure the default Android Voice Recogniser is set to Google not
Samsung/Vlingo
Uninstall any offline recognition files you already have installed
from the Google Voice Search Settings
Go to your Android Application Settings and see if you can uninstall
the updates for the Google Search and Google Voice Search
applications.
If you can't do the above, go to the Play Store see if you have the
option there.
Reboot (if you achieved 2, 3 or 4)
Update Google Search and Google Voice Search from the Play Store (if
you achieved 3 or 4 or if an update is available anyway).
Reboot (if you achieved 6)
Install English UK offline language files
Reboot
Use utter! with a connection
Switch to aeroplane mode and give it a try
Once it is working, the offline recognition of other languages,
such as English US should start working too.
EDIT: Temporarily changing the device locale to English UK also seems to kickstart this to work for some.
Some users reported they still had to reboot a number of times before it would begin working, but they all get there eventually, often inexplicably to what was the trigger, the key to which are inside the Google Search APK, so not in the public domain or part of AOSP.
From what I can establish, Google tests the availability of a connection prior to deciding whether to use offline or online recognition. If a connection is available initially but is lost prior to the response, Google will supply a connection error, it won’t fall-back to offline. As a side note, if a request for the network synthesised voice has been made, there is no error supplied it if fails – You get silence.
The Google Search update enabled no additional features in Google Now and in fact if you try to use it with no internet connection, it will error. I mention this as I wondered if the ability would be withdrawn as quietly as it appeared and therefore shouldn't be relied upon in production.
If you intend to start using the SpeechRecognizer class, be warned, there is a pretty major bug associated with it, which require your own implementation to handle.
Not being able to specifically request offline = true, makes controlling this feature impossible without manipulating the data connection. Rubbish. You’ll get hundreds of user emails asking you why you haven’t enabled something so simple!
EDIT: Since API level 23 a new parameter has been added EXTRA_PREFER_OFFLINE which the Google recognition service does appear to adhere to.
Hope the above helps.
I would like to improve the guide that the answer https://stackoverflow.com/a/17674655/2987828 sends to its users, with images. It is the sentence "For those that it doesn't, this is the ‘guide’ I supply them with." that I want to improve.
The user should click on the four buttons highlighted in blue in these images:
Then the user can select any desired languages. When the download is done, he should disconnect from network, and then click on the "microphone" button of the keyboard.
It worked for me (android 4.1.2), then language recognition worked out of the box, without rebooting. I can now dictates instructions to the shell of Terminal Emulator ! And it is twice faster offline than online, on a padfone 2 from ASUS.
These images are licensed under cc by-sa 3.0 with attribution required to stackoverflow.com/a/21329845/2987828 ; you may hence add these images anywhere along with this attribution.
(This the standard policy of all images and texts at stackoverflow.com)
A simple and flexible offline recognition on Android is implemented by CMUSphinx, an open source speech recognition toolkit. It works purely offline, fast and configurable It can listen continuously for keyword, for example.
You can find latest code and tutorial here.
Update in 2019: Time goes fast, CMUSphinx is not that accurate anymore. I recommend to try Kaldi toolkit instead. The demo is here.
In short, I don't have the implementation, but the explanation.
Google did not make offline speech recognition available to third party apps. Offline recognition is only accessable via the keyboard. Ben Randall (the developer of utter!) explains his workaround in an article at Android Police:
I had implemented my own keyboard and was switching between Google
Voice Typing and the users default keyboard with an invisible edit
text field and transparent Activity to get the input. Dirty hack!
This was the only way to do it, as offline Voice Typing could only be
triggered by an IME or a system application (that was my root hack) .
The other type of recognition API … didn't trigger it and just failed
with a server error. … A lot of work wasted for me on the workaround!
But at least I was ready for the implementation...
From Utter! Claims To Be The First Non-IME App To Utilize Offline Voice Recognition In Jelly Bean
I successfully implemented my Speech-Service with offline capabilities by using onPartialResults when offline and onResults when online.
I was dealing with this and I noticed that you need to install the offline package for your Language. My language setting was "Español (Estados Unidos)" but there is not offline package for that language, so when I turned off all network connectivity I was getting an alert from RecognizerIntent saying that can't reach Google, then I change the language to "English (US)" (because I already have the offline package) and launched the RecognizerIntent it just worked out.
Keys: Language setting == Offline Voice Recognizer Package
It is apparently possible to manually install offline voice recognition by downloading the files directly and installing them in the right locations manually. I guess this is just a way to bypass Google hardware requirements.
However, personally I didn't have to reboot or anything, simply changing to UK and back again did it.
Working example is given below,
MyService.class
public class MyService extends Service implements SpeechDelegate, Speech.stopDueToDelay {
public static SpeechDelegate delegate;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//TODO do something useful
try {
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
((AudioManager) Objects.requireNonNull(
getSystemService(Context.AUDIO_SERVICE))).setStreamMute(AudioManager.STREAM_SYSTEM, true);
}
} catch (Exception e) {
e.printStackTrace();
}
Speech.init(this);
delegate = this;
Speech.getInstance().setListener(this);
if (Speech.getInstance().isListening()) {
Speech.getInstance().stopListening();
} else {
System.setProperty("rx.unsafe-disable", "True");
RxPermissions.getInstance(this).request(permission.RECORD_AUDIO).subscribe(granted -> {
if (granted) { // Always true pre-M
try {
Speech.getInstance().stopTextToSpeech();
Speech.getInstance().startListening(null, this);
} catch (SpeechRecognitionNotAvailable exc) {
//showSpeechNotSupportedDialog();
} catch (GoogleVoiceTypingDisabledException exc) {
//showEnableGoogleVoiceTyping();
}
} else {
Toast.makeText(this, R.string.permission_required, Toast.LENGTH_LONG).show();
}
});
}
return Service.START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
//TODO for communication return IBinder implementation
return null;
}
#Override
public void onStartOfSpeech() {
}
#Override
public void onSpeechRmsChanged(float value) {
}
#Override
public void onSpeechPartialResults(List<String> results) {
for (String partial : results) {
Log.d("Result", partial+"");
}
}
#Override
public void onSpeechResult(String result) {
Log.d("Result", result+"");
if (!TextUtils.isEmpty(result)) {
Toast.makeText(this, result, Toast.LENGTH_SHORT).show();
}
}
#Override
public void onSpecifiedCommandPronounced(String event) {
try {
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
((AudioManager) Objects.requireNonNull(
getSystemService(Context.AUDIO_SERVICE))).setStreamMute(AudioManager.STREAM_SYSTEM, true);
}
} catch (Exception e) {
e.printStackTrace();
}
if (Speech.getInstance().isListening()) {
Speech.getInstance().stopListening();
} else {
RxPermissions.getInstance(this).request(permission.RECORD_AUDIO).subscribe(granted -> {
if (granted) { // Always true pre-M
try {
Speech.getInstance().stopTextToSpeech();
Speech.getInstance().startListening(null, this);
} catch (SpeechRecognitionNotAvailable exc) {
//showSpeechNotSupportedDialog();
} catch (GoogleVoiceTypingDisabledException exc) {
//showEnableGoogleVoiceTyping();
}
} else {
Toast.makeText(this, R.string.permission_required, Toast.LENGTH_LONG).show();
}
});
}
}
#Override
public void onTaskRemoved(Intent rootIntent) {
//Restarting the service if it is removed.
PendingIntent service =
PendingIntent.getService(getApplicationContext(), new Random().nextInt(),
new Intent(getApplicationContext(), MyService.class), PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
assert alarmManager != null;
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, service);
super.onTaskRemoved(rootIntent);
}
}
For more details,
https://github.com/sachinvarma/Speech-Recognizer
Hope this will help someone in future.
I've been working on an android app concept in which the app has to auto-dial some special USSD codes in order to initiate certain telco services of interest to the user when the user initiates the service via a shortcut in the app.
The trouble I'm finding is that when the app tries to auto-dial such short codes or USSD numbers, the phone's OS (or is it the Call Intent), doesn't auto-dial, but instead presents the user with the code/number in the dial-pad and so the user has to manually initiate the call - which sort of defeats my intention of allowing users to initiate the services with just one click - the shortcut.
Currently, this is how I'm initiating these calls:
intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:" + number.trim()));
try {
activity.startActivity(intent);
} catch (Exception e) {
Log.d(Tag, e.getMessage());
}
Interestingly, a number such as +256772777000 will auto-dial, launching the user into the call automatically, but a number/code such as 911, *112#, *1*23#, etc won't.
So, what do I need to do differently, or is this not possible at all?
UPDATE
Actually, looking at another app in which I was autodialling user-specified numbers, the problem with the above code trying to auto-dial ussd codes was that instead of using intent.ACTION_CALL, I was using intent.ACTION_DIAL - which definitely just prompts the user with the number to call, without directly calling it. When I fixed that, the app now works as expected. See answer below...
Code samples are most welcome.
Actually, despite what some people were claiming about Android preventing such a feature. When I looked at the code in one of my older apps which auto-dials user-specified numbers, I found the solution to be:
intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + number.trim()));
try {
activity.startActivity(intent);
} catch (Exception e) {
Log.d(Tag, e.getMessage());
}
This works as expected - USSD codes get auto-dialled when above code runs. The only important thing to note when using this approach, being that you have to add the following permissions to your manifest:
<uses-permission android:name="android.permission.CALL_PHONE" />
So, as indicated in the update to my question, the problem with my original approach was using intent.ACTION_DIAL instead of intent.ACTION_CALL.
These are my two methods that are called from within my main activity, in onStart() and onStop() respectively.
private void registerForMessages()
{
Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER");
registrationIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0));
registrationIntent.putExtra("sender", "my project id");
startService(registrationIntent);
}
private void unRegisterForMessages()
{
Intent unregIntent = new Intent("com.google.android.c2dm.intent.UNREGISTER");
unregIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0));
startService(unregIntent);
}
Where "my project id" is my 12 digit project number with google cloud messaging for android enabled in the google api console.
Both times that these are called, I get the following log:
08-10 15:07:19.370: D/GCM(791): [C2DMRegistrar.143] Send register result null 0 0
I have the manifest file set up exactly the way I have seen it set up for others. My receiver class is never called anyway, the error here in the registration seems to stop it there.
Does anyone have any idea what the error message even is telling me? What kind of things could be causing this error?
It's a little confusing trying to look it up on the android official guides, because some of it is possibly referring to the deprecated methods, and some instructions contradict each other - some say do it this way, others want you to use a register method for it.
have a look on Android Push Notifications using Google Cloud Messaging (GCM), PHP and MySQL
http://www.androidhive.info/2012/10/android-push-notifications-using-google-cloud-messaging-gcm-php-and-mysql/
As per google’s documentation “Google Cloud Messaging for Android (GCM) is a service that helps developers send data from servers to their Android applications on Android devices”. Using this service you can send data to your application whenever new data is available instead of making requests to server in timely fashion. Integrating GCM in your android application enhances user experience and saves lot of battery power.
may help you..
Actually it's ok. I think that method of doing it must actually be deprecated or something. This worked:
gcm = GoogleCloudMessaging.getInstance(this);
Thread thread = new Thread()
{
#Override
public void run()
{
try
{
Log.d("IM", "registration thread happening");
String regid = gcm.register("project number");
Log.d("IM", "id: " + regid);
}
catch (IOException e)
{
Log.d("IM register", "io exception: ");
e.printStackTrace();
}
}
};
thread.start();
It was giving me an IOException until I stuck it in its own thread too but seems to work ok now. I think the official guide needs updated perhaps.
I am using following code:
public class MyBillingService extends Service implements ServiceConnection
{
String TAG = "MyBillingService";
#Override
public void onCreate()
{
super.onCreate();
Log.i(TAG, "onCreate");
try {
boolean bindResult = getBaseContext().bindService(
new Intent(IMarketBillingService.class.getName()), this, Context.BIND_AUTO_CREATE);
if (bindResult) {
Log.i(TAG, "Service bind successful.");
} else {
Log.e(TAG, "Could not bind to the MarketBillingService.");
}
} catch (SecurityException e) {
Log.e(TAG, "Security exception: " + e);
}
}
}
I have also added the IMarketBillingService.aidl but still it show like:
Could not bind to the
MarketBillingService
Can you point out my mistake?
I don't know if this is the cause for your problem, but you are using bindService() on getBaseContext(). Is there a reason that you don't call bindService() on the service instance? That's what the example service does.
The base context seems to be mostly unneeded and the general advice seems to not use it.
See the other question: What's the difference between the various methods to get a Context?
Besides that, you need to have the newest (or at least a recent) version of the Android Market App on your device, although I assume that it updates itself.
There is a way to test if the market app supports In-App Billing, somewhere described in the In-App-Billing reference. I assume that it is worth to do that check.
I was testing on an old Android device which had a clean system install. This didn't have a copy of the Google Market app on it (hat tip #sstn).
So I logged in with a Google account, then launched the Market. That prompted me to agree to terms and the Google Play app appeared in the list of applications.
Then I removed and reinstalled my app and it bound to the service correctly!
on Android phones, under Call -> Additional settings -> Caller ID
it is possible to hide your caller ID. I want to do that programatically from my code, but was not able to find a way to do that.
I searched through
android.provider
android.telephony
for 2.1 release and was not able to find it.
Has anybody successfully solved this issue?
Thanks in advance. Best regards.
Here I will describe two approaches I tried.
1.) It is possible to display Additional Call Settings screen from your application. Although it looks like it is part of the Settings application, that is not true. This Activity is part of the Native Phone Application, and it may be approached with the following intent:
Intent additionalCallSettingsIntent = new Intent("android.intent.action.MAIN");
ComponentName distantActivity = new ComponentName("com.android.phone", "com.android.phone.GsmUmtsAdditionalCallOptions");
additionalCallSettingsIntent.setComponent(distantActivity);
startActivity(additionalCallSettingsIntent);
Then user has to manually press on the CallerID preference and gets radio button with 3 options.
This was not actually what I wanted to achieve when I asked this question. I wanted to avoid step where user has to select any further options.
2.) When approach described under 1.) is executed in the Native Phone Application, function setOutgoingCallerIdDisplay() from com.android.internal.telephony.Phone has been used.
This was the basis for the next approach: use Java Reflection on this class and try to invoke the function with appropriate parameters:
try
{
Class <?> phoneFactoryClass = Class.forName("com.android.internal.telephony.PhoneFactory");
try
{
Method getDefaultPhoneMethod = phoneFactoryClass.getDeclaredMethod("getDefaultPhone");
Method makeDefaultPhoneMethod = phoneFactoryClass.getMethod("makeDefaultPhone" , Context.class);
try
{
makeDefaultPhoneMethod.invoke(null, this);
Object defaultPhone = getDefaultPhoneMethod.invoke(null);
Class <?> phoneInterface = Class.forName("com.android.internal.telephony.Phone");
Method getPhoneServiceMethod = phoneInterface.getMethod("setOutgoingCallerIdDisplay", int.class, Message.class);
getPhoneServiceMethod.invoke(defaultPhone, 1, null);
}
catch (InvocationTargetException ex)
{
ex.printStackTrace();
}
catch (IllegalAccessException ex)
{
ex.printStackTrace();
}
}
catch (NoSuchMethodException ex)
{
ex.printStackTrace();
}
}
catch (ClassNotFoundException ex)
{
ex.printStackTrace();
}
Firstly I tried just to use getDefaultPhone(), but I get RuntimeException
"PhoneFactory.getDefaultPhone must be called from Looper thread"
Obviously, issue lies in the fact that I tried to call this method from the Message Loop that was not the Native Phone App one.
Tried to avoid this by making own default phone, but this was a security violation:
ERROR/AndroidRuntime(2338): java.lang.SecurityException: Permission Denial: not allowed to send broadcast android.provider.Telephony.SPN_STRINGS_UPDATED from pid=2338, uid=10048
The only way to overcome (both of) this would be to sign your app with the same key as the core systems app, as described under
Run secure API calls as root, android
I'm not sure if this is a global feature, but Australian phones can hide their number by prefixing the caller's number with #31# or 1831. This may not be the perfect solution, but a prefix like this could possibly work for your requirements during coding.