Previous post "Connect HM-10 (BLE Module) to Android device, with BluetoothLeGatt sample project" show step-by-step to modify Android BluetoothLeGatt sample program, by-pass Heart Rate Measurement profile handling to show function of HM-10.
Here we will send dummy data of Heart Rate Measurement profile by Arduino Due + HM-10.
First of all, edit BluetoothLeService.java to resume original handling for Heart Rate Measurement profile:
/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
/** * Service for managing connection and data communication with a GATT server hosted on a * given Bluetooth LE device. */ public class BluetoothLeService extends Service { private final static String TAG = BluetoothLeService.class.getSimpleName();
private static final int STATE_DISCONNECTED = 0; private static final int STATE_CONNECTING = 1; private static final int STATE_CONNECTED = 2;
public final static String ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; public final static String ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED"; public final static String ACTION_DATA_AVAILABLE = "com.example.bluetooth.le.ACTION_DATA_AVAILABLE"; public final static String EXTRA_DATA = "com.example.bluetooth.le.EXTRA_DATA";
public final static UUID UUID_HEART_RATE_MEASUREMENT = UUID.fromString(SampleGattAttributes.HM_10);
// Implements callback methods for GATT events that the app cares about. For example, // connection change and services discovered. private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { String intentAction; if (newState == BluetoothProfile.STATE_CONNECTED) { intentAction = ACTION_GATT_CONNECTED; mConnectionState = STATE_CONNECTED; broadcastUpdate(intentAction); Log.i(TAG, "Connected to GATT server."); // Attempts to discover services after successful connection. Log.i(TAG, "Attempting to start service discovery:" + mBluetoothGatt.discoverServices());
private void broadcastUpdate(final String action) { final Intent intent = new Intent(action); sendBroadcast(intent); }
private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) { final Intent intent = new Intent(action);
// This is special handling for the Heart Rate Measurement profile. Data parsing is // carried out as per profile specifications: // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); int format = -1; if ((flag & 0x01) != 0) { format = BluetoothGattCharacteristic.FORMAT_UINT16; Log.d(TAG, "Heart rate format UINT16."); } else { format = BluetoothGattCharacteristic.FORMAT_UINT8; Log.d(TAG, "Heart rate format UINT8."); } final int heartRate = characteristic.getIntValue(format, 1); Log.d(TAG, String.format("Received heart rate: %d", heartRate)); intent.putExtra(EXTRA_DATA, String.valueOf(heartRate)); } else { // For all other profiles, writes the data formatted in HEX. final byte[] data = characteristic.getValue(); if (data != null && data.length > 0) { final StringBuilder stringBuilder = new StringBuilder(data.length); for(byte byteChar : data) stringBuilder.append(String.format("%02X ", byteChar)); intent.putExtra(EXTRA_DATA, new String(data) + " " + stringBuilder.toString()); } }
public class LocalBinder extends Binder { BluetoothLeService getService() { return BluetoothLeService.this; } }
@Override public IBinder onBind(Intent intent) { return mBinder; }
@Override public boolean onUnbind(Intent intent) { // After using a given device, you should make sure that BluetoothGatt.close() is called // such that resources are cleaned up properly. In this particular example, close() is // invoked when the UI is disconnected from the Service. close(); return super.onUnbind(intent); }
private final IBinder mBinder = new LocalBinder();
/** * Initializes a reference to the local Bluetooth adapter. * * @return Return true if the initialization is successful. */ public boolean initialize() { // For API level 18 and above, get a reference to BluetoothAdapter through // BluetoothManager. if (mBluetoothManager == null) { mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); if (mBluetoothManager == null) { Log.e(TAG, "Unable to initialize BluetoothManager."); return false; } }
mBluetoothAdapter = mBluetoothManager.getAdapter(); if (mBluetoothAdapter == null) { Log.e(TAG, "Unable to obtain a BluetoothAdapter."); return false; }
return true; }
/** * Connects to the GATT server hosted on the Bluetooth LE device. * * @param address The device address of the destination device. * * @return Return true if the connection is initiated successfully. The connection result * is reported asynchronously through the * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} * callback. */ public boolean connect(final String address) { if (mBluetoothAdapter == null || address == null) { Log.w(TAG, "BluetoothAdapter not initialized or unspecified address."); return false; }
// Previously connected device. Try to reconnect. if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) { Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection."); if (mBluetoothGatt.connect()) { mConnectionState = STATE_CONNECTING; return true; } else { return false; } }
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); if (device == null) { Log.w(TAG, "Device not found. Unable to connect."); return false; } // We want to directly connect to the device, so we are setting the autoConnect // parameter to false. mBluetoothGatt = device.connectGatt(this, false, mGattCallback); Log.d(TAG, "Trying to create a new connection."); mBluetoothDeviceAddress = address; mConnectionState = STATE_CONNECTING; return true; }
/** * Disconnects an existing connection or cancel a pending connection. The disconnection result * is reported asynchronously through the * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)} * callback. */ public void disconnect() { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } mBluetoothGatt.disconnect(); }
/** * After using a given BLE device, the app must call this method to ensure resources are * released properly. */ public void close() { if (mBluetoothGatt == null) { return; } mBluetoothGatt.close(); mBluetoothGatt = null; }
/** * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)} * callback. * * @param characteristic The characteristic to read from. */ public void readCharacteristic(BluetoothGattCharacteristic characteristic) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } mBluetoothGatt.readCharacteristic(characteristic); }
/** * Enables or disables notification on a give characteristic. * * @param characteristic Characteristic to act on. * @param enabled If true, enable notification. False otherwise. */ public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) { if (mBluetoothAdapter == null || mBluetoothGatt == null) { Log.w(TAG, "BluetoothAdapter not initialized"); return; } mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
// This is specific to Heart Rate Measurement. if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) { BluetoothGattDescriptor descriptor = characteristic.getDescriptor( UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG)); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); } }
/** * Retrieves a list of supported GATT services on the connected device. This should be * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully. * * @return A {@code List} of supported services. */ public List<BluetoothGattService> getSupportedGattServices() { if (mBluetoothGatt == null) return null;
return mBluetoothGatt.getServices(); } }
Connect Arduino Due and HM-10: HM-10 VCC - Arduino Due 3.3V HM-10 GND - Arduino Due GND HM-10 Tx - Arduino Due Rx HM-10 Rx - Arduino Due Tx
Program on Arduino Due, DueSimHeartRate.ino. It only send dummy heart rate data to serial port.
Editors note: Its been just over a year since we launched Chromebox for meetings, and to celebrate the milestone were sharing stories about our customers and their approaches to business, culture and productivity that are bringing them success. In todays post, online accounting software provider Xero tells how it manages to keep its startup-like efficiency, innovation and feel while expanding globally. To learn more about Chromebox for meetings, join us online at Chrome Live on April 22 and see how companies scale face-to-face meetings across the globe.
Xero was started by several developers nine years ago in an apartment above a coffee shop in Wellington, New Zealand. Today, we have more than 1,000 employees in 15 cities across the U.S., U.K., Australia and New Zealand and provide online accounting software to more than 400,000 global customers. With more than 200 percent five-year average sales growth as of June 2014, our biggest challenge now is managing the fast-paced growth while maintaining our nimble, tech-forward startup culture.
We like to keep work in small groups and move quickly. Our teams work closely on projects even when theyre located in different offices around the world. And since we like to stay on the cutting edge of technology, were using Google Apps, which allows us to stay coordinated and productive.
Our pain point in IT was finding a way for teams in different cities and offices to meet and collaborate at the same time. We used a variety of video conferencing technologies, including PCs, HDMI/VGA and projectors. They were difficult to set up, meetings were delayed and productivity suffered. As we continued to grow, this struggle intensified, and we realized that we needed to find a solution fast. We needed to streamline our meeting room setups and get the most out of Hangouts. When we heard about Chromebox for meetings, we jumped at the chance to try it out.
We started with six Chromebox for meetings units. Today, we have nearly a hundred. Theyre in every meeting room. We use them for room-to-room conferencing and all hands meetings. The global team uses them to connect every two weeks and the CEO addresses the entire company via Hangout on Air.
Chromebox for meetings allow us to keep things simple. Theres very little infrastructure or wireless connections needed on our side, so no cables necessary. Setup is fast and the integration with Gmail makes joining Hangouts as easy as clicking a button. Its easy to share documents and work on them together. Then theres the cost savings. Instead of spending between $40,000 and $60,000 on a video conferencing system, we spent one-tenth of that on a Chromebox and a display.
We may be a larger company now, but we still want to move and act quickly. No matter how large we become, our values align with those of fresh innovative companies that respond rapidly to market demand, customer needs and competition. Thanks to Chromebox for meetings, we can keep the startup feel and agility while growing at breakneck speed.
This example show how to convert bitmap to grayscale using matrix and ColorMatrixColorFilter. Once photo loaded, touch on the ImageView will show the original photo, touch release to show the grayscale photo.
//Custom color matrix to convert to GrayScale float[] matrix = new float[]{ 0.3f, 0.59f, 0.11f, 0, 0, 0.3f, 0.59f, 0.11f, 0, 0, 0.3f, 0.59f, 0.11f, 0, 0, 0, 0, 0, 1, 0,};
Bitmap dest = Bitmap.createBitmap( src.getWidth(), src.getHeight(), src.getConfig());
Canvas canvas = new Canvas(dest); Paint paint = new Paint(); ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix); paint.setColorFilter(filter); canvas.drawBitmap(src, 0, 0, paint);
return dest; }
/* reference: Load scaled bitmap http://android-er.blogspot.com/2013/08/load-scaled-bitmap.html */ private Bitmap loadScaledBitmap(Uri src, int req_w, int req_h) throws FileNotFoundException {
Bitmap bm = null;
// First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(getBaseContext().getContentResolver().openInputStream(src), null, options);
// Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; bm = BitmapFactory.decodeStream( getBaseContext().getContentResolver().openInputStream(src), null, options);
return bm; }
public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// Calculate ratios of height and width to requested height and // width final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will // guarantee // a final image with both dimensions larger than or equal to the // requested height and width. inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; }
This is a step by step guide to upgrade ones source code that was developed for an earlier version of android SDK, as is to work on a new version of SDK installed on your development environment
Pre-requisites: 1. You are using Eclipse IDE - Ganymede 2. You have installed SDK 2.1 on your machine 3. You have upgraded ADT to 0.96 on Eclipse IDE 4. You have pointed the Eclipse IDE to the new SDK 2.1 5. You have changed the default JDK compiler version on Eclipse to 1.6 6. You have created an Android Virtual Device (AVD) that uses the SDK 2.1 / Google API
NOTE: Upgrading SDK and Eclipse installations itself is provided in detail at http://developer.android.com/sdk/index.html & http://developer.android.com/sdk/installing.html
For each project that you have in your eclipse workspace that was written for the earlier version of SDK, you need to do the following for basic upgrade (this does not include the upgrade of APIs that have be deprecated):
Step-by-step guide 1. Right click the project and go to Project Properties -> Android 2. You wil see tha right pane showing the Project Build Target 3. In this pane you will see either or both: Android 2.1 & Google APIs depending on what you chose to install when upgrading your ADT to 0.96 4. Select Google Android 2.1 or Google API whichever is required by your earlier project (note you might have used only Android 1.5 for most projects. You would need Google API only for those projects that use google maps) 5. Then, go to the Project menu and click on clean. Note, this is an optional step. You may have to do this if you get the error Conversion to Dalvik format failed with error 1. 6. Then, build the project by going to Project -> build 7. You are done. You can now run the earlier Android application using the new SDK 2.1
Posted by Flint Waters, CIO of the State of Wyoming
Editors note: Todays guest blogger is Flint Waters, CIO of the State of Wyoming. Since outfitting its conference rooms with Chromebox for meetings, the state spends far less on video conferencing and has transformed how teams communicate and connect with each other and the citizens they serve. See how Wyoming and many other institutions and organizations are using Chromebox for meetings to create a culture of collaboration that translates to greater productivity and better service.
When I came to work for the State of Wyoming four years ago, five people reported to a contract CIO and operations happened at government speed. The department drew in 300 people from other agencies, and IT teams were assigned to different opportunities. I wanted consolidated IT rather than siloed and needed a culture of urgency and innovation for the state, so we began looking at tools to help us do that. Building on the efforts of the previous administration, the Governor moved all state employees to Google Apps for Work. Today, Chromebox for meetings improves transparency and brings public servants, citizens and elected officials closer together.
Before moving to Chromebox for meetings, we spent $1.5 million a year on a legacy video conferencing system. I felt we werent getting enough capability with the technology for the amount of money we were spending. So we phased out all the Tandberg systems and got 178 Chromebox for meetings licenses using a small fraction of our budget.
The cost savings is tremendous, but were even more thrilled with the way Chromebox for meetings transforms how we do our jobs and think about public service. Specifically, this technology cuts down on bureaucratic processes and hierarchical protocol. Everyone from the Governor and executives to agency directors use Chromebox for team, cabinet-level and all-hands meetings. Participants can comment and ask questions regardless of their location. Were also introducing Chromebox for meetings throughout the school system to improve communication between teachers, administrators and students.
With Chromebox for meetings, were opening up meetings and making them less formal. We have Chromeboxes in our halls, allowing people to gather around and have impromptu meetings that anybody can join the closest thing to a watercooler conversation you can get over the Internet. Its also easier to work together on documents that are viewable on monitors at the stations, making meetings even more interactive and productive.
This technology has also reduced peoples travel time and increased productivity. Wyoming is a large state with a small population and a lot of open road between cities you can drive for miles on the highway without seeing another car. Chromebox for meetings shortens the distance between offices by allowing people to have a face-to-face interaction without getting in their cars.
I get most excited about the fact that we can be collaborative with Chromebox for meetings. In the future, Id like to set up an online help desk via live Hangout thats accessible through the states website, so that when web visitors need help, wed be there to help them right away. Were connecting people at all levels of government to each other and to the public. With Google tools we can move as fast as our ideas can take us, which is just what we need to bring startup innovation and agility into the halls of government.
Last example show "Get ISO country code for the given latitude/longitude, using GeoNames Web Service, using HttpURLConnection". GeoNames provide Java Client for GeoNames Webservices to help developers to easily access the geonames web services with java. This post show how to use it in Android.
To use Java Client for GeoNames Webservices in you Android Studio project, you have to download both geonames-1.1.13.jar and jdom-1.0.jar to your local machine. Visit http://www.geonames.org/source-code/ to download.
Then you have to add the JAR modules in your Android Studio Project, refer to the video.
dependencies of :geonames-1.1.13 and :jdom-1.0 will be added in your build.gradle.
Example to get ISO country code for the given latitude/longitude, using GeoNames Java Client:
try{ lat = Double.parseDouble(strLat); }catch (NumberFormatException ex){ parsable = false; Toast.makeText(MainActivity.this, "Latitude does not contain a parsable double", Toast.LENGTH_LONG).show(); }
try{ lon = Double.parseDouble(strLon); }catch (NumberFormatException ex){ parsable = false; Toast.makeText(MainActivity.this, "Longitude does not contain a parsable double", Toast.LENGTH_LONG).show(); }
if(parsable){ new GeoNamesTask(textResult).execute(lat, lon); }
} }); }
private class GeoNamesTask extends AsyncTask<Double, Void, String> { TextView tResult;
public GeoNamesTask(TextView vResult){ tResult = vResult; tResult.setText(""); }
/* Do not use the demo account for your app or your tests. It is only meant for the sample links on the documentation pages. Create your own account instead. */ WebService.setUserName("demo");
The GeoNames geographical database covers all countries and contains over eight million placenames that are available for download free of charge. We can get the iso country code for any given latitude/longitude, using GeoNames webservices; api.geonames.org/countryCode?
CountryCode / reverse geocoding
The iso country code of any given point. Webservice Type : REST Url : api.geonames.org/countryCode? Parameters : lat,lng, type, lang, radius (buffer in km for closest country in coastal areas, a positive buffer expands the positiv area whereas a negative buffer reduces it); Result : returns the iso country code for the given latitude/longitude With the parameter type=xml this service returns an xml document with iso country code and country name. The optional parameter lang can be used to specify the language the country name should be in. JSON output is produced with type=JSON Example http://api.geonames.org/countryCode?lat=47.03&lng=10.2&username=demo
Important:
Do not use the demo account for your app or your tests. It is only meant for the sample links on the documentation pages. Create your own account instead.
The parameter username needs to be passed with each request. The username for your application can be registered here. You will then receive an email with a confirmation link and after you have confirmed the email you can enable your account for the webservice on your account page
Dont forget to url encode string parameters containing special characters or spaces. (Faq entry on url encoding)
/* Do not use the demo account for your app or your tests. It is only meant for the sample links on the documentation pages. Create your own account instead. */ String queryString = "http://api.geonames.org/countryCode?lat=" + params[0] + "&lng=" + params[1] + "&username=demo";
String s = ""; try { s = sendQuery(queryString); } catch (IOException e) { e.printStackTrace(); s = e.getMessage(); } return s; }
Next: GeoNames also provide Java Client for GeoNames Webservices to help developers to easily access the geonames web services with java. Next post "Get ISO country code for the given latitude/longitude, using GeoNames Java Client" show how to use it in Android Studio project. Related: - Find addresses of given latitude and longitude using android.location.Geocoder
Last post show how to "Retrieve IP and MAC addresses of Android WiFi tethering clients from /proc/net/arp". This example show how to get vendor/manufacturer info of the associated MAC addresses.
(You can download runnable APK from link on bottom)
http://www.macvendorlookup.com/api provide API to lookup MAC Address Vendor/Manufacturer info.
Notice that this example havent handle error condition.
This example work on last post "Updated Android Studio now provide template of Blank Activity with FloatingActionButton and Snackbar", modify the default Hello World to open image with ACTION_OPEN_DOCUMENT, display on ImageView.
edit layout/activity_main.xml, to modify the icon of the FloatingActionButton, android:src inside <android.support.design.widget.FloatingActionButton>.
Different Android handsets use different CPUs, which in turn support different instruction sets. Each combination of CPU and instruction sets has its own Application Binary Interface, or ABI. The ABI defines, with great precision, how an applications machine code is supposed to interact with the system at runtime.
To identify the android device is 32-bit or 64-bit
Android is going to support 64-bit, but there are so many android devices, how do developer know current device is 32-bit or 64-bit ? Lets begin from this command: adb shell getprop ro.product.cpu.abi getprop is an android utility to retrieve a property via the android property service.