It’s extremely easy to share with Intent on Android. However, there are some apps that capture the ACTION_SEND intents, but doesn’t allow the app to pre-fill text set with EXTRA_TEXT, resulting in poor user experience.
With the following code, we can easily exclude some unwanted apps from the chooser intent:
The above code is taken from here. Enjoy and happy coding!
Osmosis is a command line application for processing Open Street Map data, with the souce code available here. The tool provides e.g. ability to generate and read data dumps, extract data inside a bounding box, etc. You can also easily write your own plugin to convert OSM data to e.g. formats your application understands.
Now, let’s start.
Write a Simple Plugin
An osmosis plugin is basically a normal jar, plus a plugin.xml file that describes how to load the plugin as shown below:
As we can see, the plugin is loaded by MyPluginLoader:
The following shows MyFactory that creates the real tasks:
Now, a simple task that does nothing but print some messages:
Just copy the generated jar to ~/.openstreetmap/osmosis/plugins folder.
Use the Plugin
You can now use your plugin: osmosis --read-pbf latest.osm.pbf --my-plugin, and you will get some output like below:
Yes, it’s that simple to create your own osmosis plugin, and time to hack now!
To scan e.g. heart rate monitors, you’re supposed to use code like this:
Here, the service UUID is to describe the service the peripheral devices provide, e.g. 0000180d-0000-1000-8000-00805f9b34fb is the service UUID for heart rate monitors.
Well, the reality is, the above two approaches may or may not work, depending on the specific device and OS version (e.g. my Samsung Galaxy S3 with Android 4.3 doesn’t support the first filter-based approach). It’s a known issue, and only fixed in Android 5.0 (only checked with Nexus 5, and seems working fine).
So, in your code, you have to scan devices with both of the ways, and hope that one shall work. With the second approach, you have to check if the scanned device is your target device through device names or even connect to it and figure out the services it provides.
Sometimes, you might need to involve the users. First, ask your users to turn off WiFi and try again. Not working? Turn off Bluetooth and on again and give a try. Still not working? OK, the Bluetooth service is crashed and can’t be restarted unless you reboot the device. So, just reboot your device like you often do with your Windows PC, then you shouldTM be fine.
If you’ve paired with BLE devices, there’s a known bug that Android might forget the status, and you have to pair again.
BLE and Classic Bluetooth
So you want to connect to both BLE devices and classic Bluetooth devices at the same time? Obviously, you’re asking too much. My experience is, the classic Bluetooth devices are more likely to be disconnected, and they won’t be able to connect until you ask the users to act as described above.
I haven’t figured any robust ways to get them always working. Please let me know if you’ve found solutions.
Too Many BLE Devices
If the user has scanned too many devices, he / she will be notified Unfortunately, Bluetooth share has stopped. And of course it’s a system issue, and it sucks especially when there’re BLE beacons changing address all the time.
OK, there’s a non-perfect solution. Also, for non-rooted devices, the user can turn off the Bluetooth to postpone the issue from happening. Alternatively, the user can do a factory reset when shit hits the fan, and wait for it to happen the next time. For rooted devices, you can manually edit the bt_config.xml file.
Best solution? Get a device with Android 4.4.3 or later.
There is some limitation on the number of BLE devices Android can connect to at the same time, and it seems the number is different on different versions. So better connect one by one.
This is not an Android issue, but more like a Samsung issue (works fine on Nexus, but haven’t checked other vendors):
Disconnect from GATT Server
So, hopefully, you have successfully connected to your GATT server, and after all the reading and writing, you need to disconnect:
Well, it may or may not work, and might throw exceptions, so better catch all Throwables.
Well, it’s a big mess in 4.3 and 4.4 (thanks to the new Bluedroid Bluetooth stack they introduced in 4.2), but luckily things seem to be more stable in 5.0. And hopefully, most devices could enjoy Lollipop somewhere in the future.
Among others, you will need to add the wearable module to your build.gradle file for both wearable app project and phone app project:
Then you should include your wearable app project into your phone app project:
Basic wearable app
Basically, you can run any Android application on your watch, though it has a small display and limited hardware support (e.g. some watch has no GPS support). You can also use the support library provided by Google for some common UI widgets:
This library also provides the handy way to load a different layout for square or round watches. In your main activity’s layout file:
The WatchViewStub view will load the corresponding layout based on the shape of the watch. Then in your activity:
Send and sync data
There are two ways to share data between your phone and watch:
Send a specific message to a certain node using the MessageApi.
Share data among all nodes using the DataApi. With this API, the data sent will be synchronized across all connected devices, which means the data will be pushed to a disconnected device if it gets connected later.
With both methods, the data are private to the application, so the develop doesn’t need to worry about the privacy nor security.
Send data with MessageApi
Once you have a connected GoogleApiClient, you can use the following code to send a message to a connected node:
Sync data with DataApi
Once you have a connected GoogleApiClient, you can use the following code to sync data across all connected nodes:
Receive data with WearableListenerService
To receive messages or data updates from other nodes, you must extend the WearableListenerService, whose life cycle is managed by the phone or the watch:
And register it in your AndroidManifest.xml:
Alternatively, for an activity or a fragment, you can use DataApi or MessageApi to register a temporary listener.
Yep, that’s it. Quite straight forward to get your app running on the watch, happy hacking!
If you haven’t set up Google Play services yet, please follow this tutorial.
First, update your AndroidManifest.xml file to request the permissions for accessing locations:
The two permissions allow you to control the accuracy of the requested locations, and you don’t have to request both for your app. If you only request the coarse location permission, the fetched location will be obfuscated. However, if you want to use the geofencing feature, you must request the ACCESS_FINE_LOCATION permission.
Connect Location Client
With the new GoogleApiClient class, you can connect all needed services at once, and Google Play services will handle all the permission requests, etc.:
Access Current Location
Once connected, you can easily fetch the current location:
Note that this getLastLocation() method might return null in case location is not available, though this happens very rarely. Also, it might return a location that is a bit old, so the client should check it manually.
Listen to Location Updates
Let’s extend the above code snippet:
The difference between setFastestInterval() and setInterval() is:
If the location updates is retrieved by other apps (or other location request in your app), your onLocationChanged() callback here won’t be called more frequently than the time set by setFastestInterval().
On the other hand, the location client will actively try to get location updates at the interval set by setInterval(), which has a direct impact on the power consumption of your app.
Then how about background location tracking? Do I need to implement a long-running Service myself? The answer is simply, no.
With this, your listener (it can be an IntentService, or a BroadcastReceiver) as defined in the PendingIntent will be triggered even if your app is killed by the system. The location updated will be sent with key FusedLocationProviderApi.KEY_LOCATION_CHANGED and a Location object as the value in the Intent:
With geofencing, your app can be notified when the device enters, stays in, or exits a defined area. Please note that geofencing requires ACCESS_FINE_LOCATION. Again, let’s keep extending the above sample:
Here, the loitering delay means the GEOFENCE_TRANSITION_DWELL will be notified 30 seconds after the device enters the area. There’re also limitations on the number of geofences (100 per app) and pending intents (5 per app) enforced.
When a geofence transition is triggered, you can find more details easily e.g. in an IntentService:
Finally, to remove geofences, you can simply use one of the overloaded LocationServices.GeofencingApi.removeGeofences() methods.
To enable mock locations, you must first request the corresponding permission (usually for your debug build only):
Then you can enable and set mock location:
To distinguish if the location is a mock one, a key of FusedLocationProviderApi.KEY_MOCK_LOCATION in the location object’s bundle extra will be set to true.
Once done, please remember to set the mock mode to false. If you forget that, the system will set it to false when your location client is disconnected.
That’s it for today. Happy coding and keep reading.
If you haven’t set up Google Play services SDK yet, please follow this tutorial. Today, we demonstrate how to use the cloud messaging / push notification.
Enable Cloud Messaging API
First, create a project in Google Developers Console for your app, and enable the Google Cloud Messaging for Android API. The project number will be used as the GCM sender ID.
Then, create a server key in Public API access with your server’s IP address (for testing purposes, you can use e.g. 0.0.0.0/0 to allow anybody to send messages with the generated API key). The generated API key will be used to authenticate your server.
Note: For devices running Android older than 4.0.4, it requires users to set up a Google account before using GCM.
Now, update your AndroidManifest.xml file, specifying the required permissions:
Register The Client
The following snippet shows how to register a client for cloud messaging:
Note that Google’s GCM server doesn’t handle localization nor message scheduling, so the client might also need to upload e.g. the preferred language and timezone to the server to improve user experiences.
In your AndroidManifest.xml, specify a listener:
Then you can handle the message yourself:
Send a Message
To send a push message, you can simply send a POST request to Google’s GCM server. The URL of the server is: https://gcm-http.googleapis.com/gcm/send.
The HTTP header must contain the following two:
The HTTP body is a JSON object, something like this:
The fields of the data object represent the key-value pairs of the message’s payload data. With the above example, the client will receive an intent with an extra of key key_1 and value value_1, and another extra of key key_2 and value value_2.
The priority can be either normal (default) or high. The messages marked as high priority will be sent immediately, even when the device is in Doze mode. For normal priority messages, they will be batched for devices in Doze mode, and will be discarded if the message expires while the device is in Doze mode.
For a complete list of allowed fields in the posted JSON, check here.
The response can contain the following status code:
200: The message is successfully processed.
400: The request JSON object is malformed.
401: The sender fails to authenticate itself.
5xx: Server error, the sender should respect the Retry-After and retry later.
More details of how to parse the response messages can be found here.
That’s it for today. Happy hacking and keep reading!
Then get you the signing certificate’s fingerprint:
Then use the SHA-1 fingerprint and your app’s package name to generate a new key for API access from Google Developers Console.
Now update your app’s AndroidManifest.xml file, specifying the permissions, feature required, and the map key:
Add a Map
The easiest way to show a map is to add a MapFragment (or a SupportMapFragment for older devices) to your activity. Another approach is to add a MapView to your activity or fragment.
To add a map view showing satellite maps with an XML file:
Then in your activity:
Once you have the map ready, you can use the getMap() method (or the getMapAsync() to get reference to the map asynchronously) of the map view or fragment to get a GoogleMap object. If this method returns null, it means the map is not ready yet (e.g. because the Google Play services is not available). It serves as the entry point for interactions with the map:
To configure the UI, e.g. show / hide my location button, enable / disable gestures, use the UiSettings object fetched using GoogleMap.getUiSettings() method.
To convert between coordinates on the map and the location on the screen, use the Projection object fetched using GoogleMap.getProjection() method.
You can uses GoogleMap.setMyLocationEnabled() method to show or hide the blue dot representing device’s current location, but to get the current location you should follow my previous tutorial on locations.
To listen to map click events, you can simply set the corresponding listener using GoogleMap.setOnMapClickListener() or GoogleMap.setOnMapLongClickListener() method.
You can set the camera (i.e. which part of the map should be shown) using the GoogleMap.animateCamera() (with animation) or GoogleMap.moveCamera() (no animation) method. To listener to camera changes (e.g. triggered by user), you can specify the corresponding listener using GoogleMap.setOnCameraChangeListener().
Also, you can set map types (normal, satellite, etc.), enable / disable indoor maps, 3D buildings, etc. using the GoogleMap object.
Markers and Info Windows
Markers can be used to indicate locations or other important information on the map, with more detailed information shown in the info window.
The following snippet adds a marker with a custom icon to the map, and displays a default info window showing “Hello, Helsinki!”:
One thing to note when using e.g. BitmapDescriptorFactory is, you might need to use MapsInitializer to explicitly initialize the map.
To remove a marker, you can use the Marker.remove() method, or the GoogleMap.clear() method to remove all added markers, shapes, etc.
If you want to animate a marker on the map (e.g. use a custom marker to indicate current location instead of the boring blue dot), all you need is simply update its position periodically using the Marker.setPosition() method.
When the user clicks on a marker, by default it shows an info window with some title and snippet defined when the marker was added. But you can also listen to the click event:
If the onMarkerClick() method returns true, it means the click event has been consumed. Otherwise, if false is returned, it will execute the default behavior, i.e. moving the camera to the marker, and showing the info window.
To provide a customized info window:
The info window is rendered as an image. It means any subsequent changes won’t be visible unless you re-open the info window, and the info window can only listen to one click event for the whole info window.
Circles, Polygons, and Polylines
To add those shapes to the map, you basically follow the same pattern:
Ground and Tile Overlays
The main difference between these two overlays is: the ground overlay provides a single image for a fixed location on the map, while the tile overlay provides a set of images for different locations at different zoom level on the map.
To add a ground overlay:
To add a tile overlay:
That’s it for today. Happy hacking and keep reading :)