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.
Related
I have an application that according to some events, changes a normal notification to text-to-speech in order to since sometimes the phone isn't available to users, and it'll be safer not to handle the phone.
For example, when you're driving, this is dangerous, so i want to turn the notifications to text-to-speech.
I've looked for a long time some explanation for turning text-to-speech when driving, but i can't find any reference for that no where i search.
For generating text-to-speech, i have this part, which works fine :
private TextToSpeech mTextToSpeech;
public void sayText(Context context, final String message) {
mTextToSpeech = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
#Override
public void onInit(int status) {
try {
if (mTextToSpeech != null && status == TextToSpeech.SUCCESS) {
mTextToSpeech.setLanguage(Locale.US);
mTextToSpeech.speak(message, TextToSpeech.QUEUE_ADD, null);
}
} catch (Exception ex) {
System.out.print("Error handling TextToSpeech GCM notification " + ex.getMessage());
}
}
});
}
But, i don't know how to check if i'm currently driving or not.
As Ashwin suggested, you can use Activity recognition Api, but there's a downside of that, the driving samples you'll receive, has a field of 'confidence' which isn't always accurate, so you'll have to do extra work(such as check locations to see if you actually moved) in order to fully know if the user moved.
You can use google's FenceApi which allows you to define a fence of actions such as driving, walking, running, etc. This api launched recently. If you want a sample for using it, you can use this answer.
You can pull this git project (everything free), which does exactly what you want : adds to the normal notification a text-to-speech when you're driving.
In order to know whether you are driving or not you can use Activity Recognition API
Here is a great tutorial that might help you out Tutorial and Source Code
Running the code below, I create a folder with Google Drive Android API on a tablet. After a few seconds, delete that folder from a remote location on a PC. When I re-run the code, the API still thinks 'MyFolder' exists, even though it was deleted and not visible in the Google Drive app on the tablet. The folder persistance finally disappears after a while and the code works as expected. Is this expected behavior for Cloud drives?
Query query = new Query.Builder()
.addFilter(Filters.and(Filters.eq(
SearchableField.TITLE, "MyFolder"),
Filters.eq(SearchableField.TRASHED, false)))
.build();
Drive.DriveApi.query(getGoogleApiClient(), query)
.setResultCallback(new ResultCallback<DriveApi.MetadataBufferResult>() {
#Override
public void onResult(DriveApi.MetadataBufferResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Cannot create folder in the root.");
} else {
boolean isFound = false;
for(Metadata m : result.getMetadataBuffer()) {
if(!isFound) {
if (m.getTitle().equals("MyFolder")) {
showMessage("Folder exists");
isFound = true;
}
}
}
if(!isFound) {
showMessage("Folder not found; creating it.");
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("MyFolder")
.build();
Drive.DriveApi.getRootFolder(getGoogleApiClient())
.createFolder(getGoogleApiClient(), changeSet)
.setResultCallback(new ResultCallback<DriveFolder.DriveFolderResult>() {
#Override
public void onResult(DriveFolder.DriveFolderResult result) {
if (!result.getStatus().isSuccess()) {
showMessage("Error while trying to create the folder");
} else {
mThwingAlbertFolderId = result.getDriveFolder().getDriveId();
showMessage("Created a folder: " + mThwingAlbertFolderId);
}
}
});
}
}
}
});
What you are seeing, is a 'normal' behavior of the GDAA, that can be explained if you look closer at the 'Lifecycle of a Drive file' diagram (warning: I've never seen the source code, just assuming from what I observed).
See, the GDAA, unlike the REST Api, creates a layer that does its best to create caching and network traffic optimization. So, when you manipulate the file/folder from the 'outside' (like the web app), the GDAA layer has no knowledge of the fact until it initiates synchronization, controlled by it's own logic. I myself originally assumed that GooDrive has this under control by dispatching some kind of notification back to the GDAA, but it apparently is not the case. Also, some Googlers mentioned 'requestSync()' as a cure, but I never succeeded to make it work.
What you think you're doing, is polling the GooDrive. But effectively, you're polling the GDAA (local GooPlaySvcs) whose DriveId is still valid (not updated), unlike the real GooDrive object that is already gone.
This is one thing that is not clearly stated in the docs. GDAA is not the best Api for EVERY application. It's caching mechanism is great for transparently managing online/offline states, network traffic optimization. battery life, ... But in your situation, you may be better off by using the REST Api, since the response you get reflects the current GooDrive state.
I myself faced a similar situation and had to switch from the GDAA back to the REST (and replaced polling with a private GCM based notification system). Needless to say, by using the REST Api, your app gets more complex, usually requiring sync adapter / service to do the data synchronization, managing network states, ... all the stuff GDAA gives you for free).
In case you want to play with the 2 apis side-by side, there are two identical CRUD implementation you can use (GDAA, REST) on Github.
Good Luck
Google drive api does not sync immediately, That is why the deleted folders are still showing, so you have to force google drive to sync using requestSync()
Drive.DriveApi.requestSync(mGoogleApiClient).await();
I fount an example snippet here:
http://wiki.workassis.com/android-google-drive-api-deleted-folder-still-exists-in-query/
As Sean mentioned, the Drive Android API caches metadata locally to reduce bandwidth and battery usage.
When you perform an action on the device, e.g. creating a folder, we attempt to apply that action on the server as soon as possible. Though there can be delays due to action dependencies and content transfers, you will generally see the results reflected on the server very quickly.
When an action is performed on the server, e.g. by deleting a folder via the web client, this action is reflected on the device the next time the Drive Android API syncs. In order to conserve battery and bandwidth, sync frequency depends on how the API is being used as this is a priority for users.
If you need to guarantee that a sync has occurred, you can explicitly request a sync using DriveApi.requestSync() and wait on the result. This is currently rate limited to 1 per minute, which is frequently hit during testing, but should have a much smaller impact on real world usage.
Please let us know on our issue tracker if this sync behavior is causing issues for your use case so we can investigate solutions.
Google drive uses its own lifecycle for Drive api and manage all things in cache that's why if you delete some file or folder and try to access using google drive apis it is still available because it is stored in cache so you need to explicitly call requestSync() method for that then after that cache will be updated and gives you that folder or file not found.
below is code for that:
Drive.DriveApi.requestSync(mGoogleApiClient).setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(#NonNull Status status) {
Log.e("sync_status", status.toString());
if (status.getStatus().isSuccess()) {
setRootFolderDriveId();
}
}
});
and don't call Drive.DriveApi.requestSync(mGoogleApiClient).await() because your main thread will block so it will crash. use above one and after get successful callback you can do your operation on google drive because it's updated.
You can do it in main thread:
Drive.DriveApi.requestSync(mGoogleApiClient).setResultCallback(new ResultCallback<com.google.android.gms.common.api.Status>() {
#Override
public void onResult(com.google.android.gms.common.api.Status status) {
if (!status.getStatus().isSuccess()) {
Log.e("SYNCING", "ERROR" + status.getStatusMessage());
} else {
Log.e("SYNCING", "SUCCESS");
// execute your code to interact with Google Drive
}
}
});
I was having the same issue and using "Drive.DriveApi.requestSync" did the trick.
Also I suggest taking a look at https://github.com/francescocervone/RxDrive because you can concatenate the sync to other drive operations using rxandroid.
For example, this becomes a delete-and-sync operation:
Observable<Boolean> deleteFile = rxDrive.delete(file);
Observable<Void> syncDrive = rxDrive.sync();
Observable.concat(deleteFile, syncDrive);
The reason why you get listed deleted files from your query is that Google Drive has a "Trash" folder that is "searchable". You need to empty your trash first.
My Question is: Can Android 4.3 (client) have active connections with multiple BLE devices (servers)? If so, how can I achieve it?
What I did so far
I try to evaluate what throughput you can achieve using BLE and Android 4.3 BLE API. In addition I also try to find out how many devices can be connected and active at the same time. I use a Nexus 7 (2013), Android 4.4 as master and TI CC2540 Keyfob as slaves.
I wrote a simple server software for the slaves, which transmits 10000 20Byte packets through BLE notifications. I based my Android App on the Application Accelerator from the Bluetooth SIG.
It works well for one device and I can achieve around 56 kBits payload throughput at a Connection Interval of 7.5 ms. To connect to multiple slaves I followed the advice of a Nordic Employee who wrote in the Nordic Developer Zone:
Yes it's possible to handle multiple slaves with a single app. You would need to handle each slave with one BluetoothGatt instance. You would also need specific BluetoothGattCallback for each slave you connect to.
So I tried that and it partly works. I can connect to multiple slaves. I can also register for notifications on multiple slaves. The problem begins when I start the test. I receive at first notifications from all slaves, but after a couple Connection Intervals just the notifications from one device come trough. After about 10 seconds the other slaves disconnect, because they seem to reach the connection time-out. Sometimes I receive right from the start of the test just notifications from one slave.
I also tried accessing the attribute over a read operation with the same result. After a couple of reads just the answers from one device came trough.
I am aware that there are a few similar questions on this forum: Does Android 4.3 support multiple BLE device connections?, Has native Android BLE GATT implementation synchronous nature? or Ble multiple connection. But none of this answers made it clear for me, if it is possible and how to do it.
I would be very grateful for advice.
I suspect everyone adding delays is just allowing the BLE system to complete the action you have asked before you submit another one. Android's BLE system has no form of queueing. If you do
BluetoothGatt g;
g.writeDescriptor(a);
g.writeDescriptor(b);
then the first write operation will immediately be overwritten with the second one. Yes it's really stupid and the documentation should probably actually mention this.
If you insert a wait, it allows the first operation to complete before doing the second. That is a huge ugly hack though. A better solution is to implement your own queue (like Google should have). Fortunately Nordic have released one for us.
https://github.com/NordicSemiconductor/puck-central-android/tree/master/PuckCentral/app/src/main/java/no/nordicsemi/puckcentral/bluetooth/gatt
Edit: By the way this is the universal behaviour for BLE APIs. WebBluetooth behaves the same way (but Javascript does make it easier to use), and I believe iOS's BLE API also behaves the same.
Re visting the bluetooth-lowenergy problem on android: I am still using delays.
The concept: after every major action that provokes the BluetoothGattCallback (e.g. conenction, service discovery, write, read) a dealy is needed. P.S. have a look at Google example on BLE API level 19 sample for connectivity to understand how Broadcasts should be sent and get some general understanding etc...
Firstly, scan (or scan) for BluetoothDevices, populate the connectionQueue with desired devices and call initConnection().
Have a look on the following example.
private Queue<BluetoothDevice> connectionQueue = new LinkedList<BluetoothDevice>();
public void initConnection(){
if(connectionThread == null){
connectionThread = new Thread(new Runnable() {
#Override
public void run() {
connectionLoop();
connectionThread.interrupt();
connectionThread = null;
}
});
connectionThread.start();
}
}
private void connectionLoop(){
while(!connectionQueue.isEmpty()){
connectionQueue.poll().connectGatt(context, false, bleInterface.mGattCallback);
try {
Thread.sleep(250);
} catch (InterruptedException e) {}
}
}
Now if all is good, you have made connections and BluetoothGattCallback.onConnectionStateChange(BluetoothGatt gatt, int status, int newState) has been called.
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
switch(status){
case BluetoothGatt.GATT_SUCCESS:
if (newState == BluetoothProfile.STATE_CONNECTED) {
broadcastUpdate(BluetoothConstants.ACTION_GATT_CONNECTED, gatt);
}else if(newState == BluetoothProfile.STATE_DISCONNECTED){
broadcastUpdate(BluetoothConstants.ACTION_GATT_DISCONNECTED, gatt);
}
break;
}
}
protected void broadcastUpdate(String action, BluetoothGatt gatt) {
final Intent intent = new Intent(action);
intent.putExtra(BluetoothConstants.EXTRA_MAC, gatt.getDevice().getAddress());
sendBroadcast(intent);
}
P.S. sendBroadcast(intent) might need to be done like this:
Context context = activity.getBaseContext();
context.sendBroadcast(intent);
Then the broadcast is received by BroadcastReceiver.onReceive(...)
public BroadcastReceiver myUpdateReceiver = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if(BluetoothConstants.ACTION_GATT_CONNECTED.equals(action)){
//Connection made, here you can make a decision: do you want to initiate service discovery.
// P.S. If you are working with multiple devices,
// make sure that you start the service discovery
// after all desired connections are made
}
....
}
}
After doing whatever you want in the broadcast receiver, here is how I continue:
private Queue<BluetoothGatt> serviceDiscoveryQueue = new LinkedList<BluetoothGatt>();
private void initServiceDiscovery(){
if(serviceDiscoveryThread == null){
serviceDiscoveryThread = new Thread(new Runnable() {
#Override
public void run() {
serviceDiscovery();
serviceDiscoveryThread.interrupt();
serviceDiscoveryThread = null;
}
});
serviceDiscoveryThread.start();
}
}
private void serviceDiscovery(){
while(!serviceDiscoveryQueue.isEmpty()){
serviceDiscoveryQueue.poll().discoverServices();
try {
Thread.sleep(250);
} catch (InterruptedException e){}
}
}
Again, after a successful service discovery, BluetoothGattCallback.onServicesDiscovered(...) is called. Again, I send an intent to the BroadcastReceiver (this time with different action String) and it is now that you can start reading, writing and enabling notifications/indications...
P.S. If you are working with multiple devices, make sure that you start the reading, writing etc... stuff after all devices have reported that their services have been discovered.
private Queue<BluetoothGattCharacteristic> characteristicReadQueue = new LinkedList<BluetoothGattCharacteristic>();
private void startThread(){
if(initialisationThread == null){
initialisationThread = new Thread(new Runnable() {
#Override
public void run() {
loopQueues();
initialisationThread.interrupt();
initialisationThread = null;
}
});
initialisationThread.start();
}
}
private void loopQueues() {
while(!characteristicReadQueue.isEmpty()){
readCharacteristic(characteristicReadQueue.poll());
try {
Thread.sleep(BluetoothConstants.DELAY);
} catch (InterruptedException e) {}
}
// A loop for starting indications and all other stuff goes here!
}
BluetoothGattCallback will have all your incoming data from the BLE sensor. A good practice is to send a broadcast with the data to your BroadcastReceiver and handle it over there.
I am developing an app with BLE features myself. The way I managed to connect to multiple devices and turn on notifications was to implement delays.
So I make a new thread (in order not to block UI thread) and in the new thread connect and turn on notifications.
For example, after BluetoothDevice.connectGatt(); call Thread.sleep();
And add the same delay for read/write and enable/dissable notifications.
EDIT
Use wait like this so that Android dindn't reaise ANR
public static boolean waitIdle() {
int i = 300;
i /= 10;
while (--i > 0) {
if (true)
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return i > 0;
}
Unfortunately notifications in the current Android BLE stack are a bit buggy. There are some hardcoded limits and I've found some stability issues even with a single device. (I read at one point that you could only have 4 notifications... not sure if that's across all devices or per device. Trying to find the source for that info now.)
I would try switching to a polling loop (say, poll the items in question 1/sec) and seeing if you find your stability increases. I would also consider switching to a different slave device (say a HRM or the TI SensorTag) to see if there is perhaps an issue with the slave-side code (unless you can test that against iOS or another platform and confirm it isn't part of the issue).
Edit: Reference for notification limitation
Rain is right in his answer, you need delays for pretty much everything when you work with BLE in Android. I developed several apps with it and it is really necessary. By using them you avoid a lot of crashes.
In my case, I use delays after every read/write command. Doing so, you ensure you receive the response from the BLE device almost always. I do something like this: (of course everything is done in a separate thread to avoid to much work on the main thread)
readCharacteristic(myChar);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
myChar.getValue();
or:
myChar.setValue(myByte);
writeCharacteristic(myChar);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
This is really useful when you read/write several characteristics in a row... As Android is enough fast to execute the commands almost instantly, if you don't use a delay between them you may get errors or incoherent values...
Hope it helps even if it is not exactly the answer to your question.
I've built an Android app which is now on Play Market. From time to time, I make updates to it, and I'd like to let users know that a new version is available.
How can I send an update notification to the users of the app?
You do not need to do anything specific for this. Since you mentioned that you are using Google Play, the update notification is taken care of by Google Play.
You just need to update the APK with a higher versionCode and Google Play should do the rest.
Update 2020: now you can use in-app updates mechanism
Docs: https://developer.android.com/guide/playcore/in-app-updates
You can do this in a lot of ways, depending on when you want the user to be able to see that there is an update available.
If you want the user to know about the update when the app is started, just create a utility method (inside the onCreate method of your main/first Activity) that checks if a newer version is available in Google Play. If it does, display an alert dialog with a relevant message and an Intent which opens your app in Google Play when the user clicks on the positive button of the alert dialog.
If you are updating the app regularly, the user will keep getting this alert dialog every time the app is started and hence, may get irritated. Thus, this is not the best approach.
If you want the user to get a notification on the phone (and not when the user starts the app), you can use the AlarmManager class to schedule a background service which checks for an update at regular intervals. If the service finds that an upgrade is actually available, publish a notification with an intent that opens your app in Google Play.
Of course, another approach is to leave it to the OS itself. If the user has not set the "Automatically update" preference for your app, the user will get a notification regularly about an update available for your, as well as any other apps.
But not all users enable background data on their devices, so this is not completely reliable.
In the end, you must respect the users preferences. If the user does not want to automatically update the app, or does not want to see a nagging dialog box whenever he/she starts your app, don't alert the user about the update.
In my opinion, you should create a PreferenceActivity that has a preference like "Check for updates regularly", which can be set from within your app. If it is set, do the needful in your own service. May be even give the user an option to select the period after which the service will check for an update.
I hope this helps!
It is up to each phone owner if she wants to be notified on new versions by google play, and it's up to each phone's manufacturer if this is to be enabled by default.
If you however are in a situation where you "require" the user to update to the new version to be compatible with some form of protocol or you have a similar similar use case where you have a server component somewhere, you might want to notify the user of a potential version conflict in the UI based on information about what is the latest version.
This information can be grabbed directrly from google play, however as #Yahel pointed out in this question google play is a closed system with no official API, and you might need to rely on unpredictable undocumented API. There is an unofficial API library here.
This leaves only one option, which is to keep this information on your own server. If you allready have a serverside this might be trivial. Simply put the latest version in an XML file and retreive that at regular intervals from your code. If the version code is outdated, trigger the notification in your UI. Here is an example implementation for doing that.
I hope this was helpful :-)
I know this is an old question but still if people are coming here to check this question, Google is now providing official support for in-app notification for application update the full documentation can be found here
Use this : https://www.push-link.com/
Google Play will notify your users that the app has an update via the notification bar.
If you set up a notification system yourself, the likely result would be that, although the user is notified of an update sooner, when he/she goes to Google Play to install the update it will not yet be available. This is because there is a lag from the time you "publish" an app/update and the time until it appears on Play. Telling your users that there is an update when the update is unavailable would only lead to confusion and frustration.
My advice: stick with Google's update notification system and don't worry about trying to get users an update 15 minutes sooner.
Some people use Android Cloud-to-Device Messaging (C2DM) to notify their users of updates. I don't think I'd bother, since I think Google Play does a pretty good job of notifying me of updates already, and implementing C2DM adds a whole new dimension to writing an app (because it requires a server component). But maybe you want to offer your users a richer update notification than you get from Google Play.
#Davek804's answer above is wrong. android:versionCode is an integer value that represents the version of the application code, relative to other versions, so using "1.5b" there is incorrect. Use "15" (or "150") instead
Found a nice solution for your problem:
Let´s say you want to check for version updates manually on app start and notify your users for the new Update.
Step 1: Download android-market-api (not the .jar file, the full project!)
Step 2: After importing it to eclipse, write in your activity the following code:
MarketService ms = new MarketService(activity);
ms.level(MarketService.REVISION).checkVersion();
now, we need to modify MarketService.java, because it seems to be broken.
Step 3: rewrite callback method and add the following methods
protected void callback(String url, JSONObject jo, AjaxStatus status){
if(jo == null) return;
String googlePlayversion = jo.optString("version", "0");
String smartphone_version = "";
PackageInfo pInfo;
try {
pInfo = act.getPackageManager().getPackageInfo(act.getPackageName(), 0);
smartphone_version = pInfo.versionName;
} catch (NameNotFoundException e) {}
boolean new_version_avaible = compare(smartphone_version, googlePlayversion);
if(new_version_avaible){
showUpdateDialog(jo);
}
}
private static boolean compare(String v1, String v2) {
String s1 = normalisedVersion(v1);
String s2 = normalisedVersion(v2);
int cmp = s1.compareTo(s2);
String cmpStr = cmp < 0 ? "<" : cmp > 0 ? ">" : "==";
System.out.printf("result: "+"'%s' %s '%s'%n", v1, cmpStr, v2);
if(cmpStr.contains("<")){
return true;
}
if(cmpStr.contains(">")||cmpStr.contains("==")){
return false;
}
return false;
}
public static String normalisedVersion(String version) {
return normalisedVersion(version, ".", 4);
}
public static String normalisedVersion(String version, String sep, int maxWidth) {
String[] split = Pattern.compile(sep, Pattern.LITERAL).split(version);
StringBuilder sb = new StringBuilder();
for (String s : split) {
sb.append(String.format("%" + maxWidth + 's', s));
}
return sb.toString();
}
If you want to test it, modify googlePlayversion string to a higher version than your local one.
The source comparison method I used is from How do you compare two version Strings in Java?
There is also a very good approach for checking version and give user in app notification or when you want to forcefully update the application if you can decide the first connection of your app with the server.In the response of the first request you can send the current version of app stored on your server and then on client end you can take the appropriate action.
Advantages of this approach-:
1-No extra request for version no.
2-It is also applicable if you are downloading the app other than the google playstore.
3-you can also use this idea if you want to check the version at particular operation of your app ex- transaction(if you add a new payment gateway.)
Don't know if you want to walk extra miles. You can try out google appengine, which serve version number for your app and let you android app check the appengine to see if there is a new version when the application is launched. That way, it does not matter if your app is in google play market nor amazon app store nor if it is installed on the phone without those two via sideloading. It is not very hard to setup appengine just for serving your application version in json. Replace "Hello World" string with your app version name ...
This can be using a simple webservice just this is one of the way to acheive.
i.e., when ever the app launch hit that webservice with the current version of the user app and on the server you need to check whether any new version is available or not(Must maintain the newest version of the app) and send the corresponding response to the user. If any newer version is available prompt the user to download the newest version of the application and if no newest version is available then allow the user to continue.
Hope so atleast something must be useful to you.
There are two models that are basically used to tackle the issue.
Pull Based
Push Based
Its depends on the architecture or design of particular system that determines whether pull based or push mechanism is used.
For pull based model you just make one http request to concerned server regarding the new version of application. The current application version no can be saved in SQLLite in android application. This can be given to server and new version can be checked against it at the server.
For push mechanism you can use C2DM push notification service.. details of which are given at http://code.google.com/android/c2dm/
Generally when you upload a new application to Google play most users get a notification about an update, some will have the app automatically downloaded to their device, depending on the settings they have.
If you seriously want to make a notification from your app to ask them to update (so that everyone gets the notification, whatever their Google play settings are, then you will have to make a web service which returns the number of the newest version. You can then compare that inside your app and post a notification. You could use Google App Engine ( https://developers.google.com/appengine/) because that works with eclipse and java, which you probably already have.
I would not recommend this approach as it creates a lot of work for you to provide something that most users have already got.
i think this is too late but it can be help some one
public enum AppVersionUpgradeNotifier {
INSTANCE;
private static final String TAG = "AppVersionUpdateManager";
private static final String PREFERENCES_APP_VERSION = "pref_app_version_upgrade";
private static final String KEY_LAST_VERSION = "last_version";
private SharedPreferences sharedPreferences;
private VersionUpdateListener versionUpdateListener;
private boolean isInitialized;
public static synchronized void init(Context context, VersionUpdateListener versionUpdateListener) {
if (context == null || versionUpdateListener == null) {
throw new IllegalArgumentException(TAG + " : Context or VersionUpdateListener is null");
}
if (!INSTANCE.isInitialized) {
INSTANCE.initInternal(context, versionUpdateListener);
} else {
Log.w(TAG, "Init called twice, ignoring...");
}
}
private void initInternal(Context context, VersionUpdateListener versionUpdateListener) {
this.sharedPreferences = context.getSharedPreferences(PREFERENCES_APP_VERSION, Context.MODE_PRIVATE);
this.versionUpdateListener = versionUpdateListener;
this.isInitialized = true;
checkVersionUpdate();
}
private void checkVersionUpdate() {
int lastVersion = getLastVersion();
int currentVersion = getCurrentVersion();
if (lastVersion < currentVersion) {
if (versionUpdateListener.onVersionUpdate(currentVersion, lastVersion)) {
upgradeLastVersionToCurrent();
}
}
}
private int getLastVersion() {
return sharedPreferences.getInt(KEY_LAST_VERSION, 0);
}
private int getCurrentVersion() {
return BuildConfig.VERSION_CODE;
}
public void upgradeLastVersionToCurrent() {
sharedPreferences.edit().putInt(KEY_LAST_VERSION, getCurrentVersion()).apply();
}
public interface VersionUpdateListener {
boolean onVersionUpdate(int newVersion, int oldVersion);
}
}
use it on
public class MyApplication extends Application implements AppVersionUpgradeNotifier.VersionUpdateListener {
#Override
public void onCreate() {
super.onCreate();
AppVersionUpgradeNotifier.init(this,this);
}
#Override
public boolean onVersionUpdate(int newVersion, int oldVersion) {
//do what you want
return true;
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1.5b"
android:versionName="1.5b">
When you re-upload your app to Google Play, if these two attributes have been changed from the previous upload, Google Play will automatically send notifications to users who have installed your app. This is the AndroidManifest file.
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!