In order to use the ActiveLook SDK for android, you should have Android Studio installed.
Copyright 2021 Microoled
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.
You can start displaying custom data in your glasses in less than 10 minutes. Ready?
You will need the following:
- A pair of glasses
- AndroidStudio up
- An android device with BLE
Open AndroidStudio and create a new project.
The ActiveLook SDK is available on jitpack.
In order to use it, you need to add jitpack repository in your project dependency resolver.
You need to add the line maven { url 'https://jitpack.io' }
.
Depending on your gradle version, you'll have to either modify the root build.gradle
or the settings.gradle
file.
- For the
build.gradle
file, you should add:
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
- For the
settings.gradle
file, you should add:
dependencyResolutionManagement {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
You can add the dependency in your application by modifying the application build.gradle
and add:
dependencies {
implementation 'com.github.activelook:android-sdk:v4.4.3'
}
On your android device, enable the developer mode and connect it to your computer via USB. Your device will prompt you to accept debugging from Android Studio. Accept it if you trust your computer and your device should appear in Android Studio menu bar, next to the 'Play' button.
You can modify the main activity of your application:
// ... package statement and other imports
import com.activelook.activelooksdk.Sdk;
import com.activelook.activelooksdk.types.Rotation;
// ... class definition and other methods
@Override
protected void onCreate(Bundle savedInstanceState) {
// ... method other statements
runOnUiThread(() -> {
Log.e("ANDROID", "Permissions");
ActivityCompat.requestPermissions(MainActivity.this, new String[] {
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.INTERNET,
Manifest.permission.WAKE_LOCK,
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN
}, 0);
Log.e("SDK", "Init");
Sdk.init(
MainActivity.this,
"your-sdk-token-provided-by-microoled",
gu -> Log.d("GLASSES_UPDATE", String.format("onUpdateStart : %s", gu)),
gu_f -> {
Log.d("GLASSES_UPDATE", String.format("onUpdateAvailableCallback : %s", gu_f.first));
gu_f.second.run();
},
gu -> Log.d("GLASSES_UPDATE", String.format("onUpdateProgress : %s", gu)),
gu -> Log.d("GLASSES_UPDATE", String.format("onUpdateSuccess : %s", gu)),
gu -> Log.d("GLASSES_UPDATE", String.format("onUpdateError : %s", gu))
);
Log.e("SDK", "Instance");
Sdk sdk = Sdk.getInstance();
Log.e("SDK", "Scan");
sdk.startScan(discoveredGlasses -> {
Log.e("DISCOVER", String.format("Glasses connecting: %s", discoveredGlasses.getAddress()));
discoveredGlasses.connect(
glasses -> {
Log.e("CONNECT", "Glasses connecting");
glasses.txt(new Point(50, 50), Rotation.TOP_LR, (byte) 0x00, (byte) 0xFF, "Mon Super Test");
Log.e("CONNECT", "Glasses connected");
},
errorDiscoveredGlasses -> {
Log.e("ERROR", "Glasses could not be connected");
},
glasses -> {
Log.e("DISCONNECT", "Glasses have been disconnected");
}
);
});
});
// ... method other statements
}
// ... other class methods
You can check our demo application on https://github.com/ActiveLook/demo-app
To run the example project, clone the repo, and import the project in Android Studio.
Note: BlueTooth will not work on the android simulator. A physical device should be used instead.
To start using the SDK, first import and initialize the sdk.
You will need a token provided by Microoled to authenticate with the update server.
Five callbacks onUpdateStart
, onUpdateAvailableCallback
, onUpdateProgress
, onUpdateSuccess
, onUpdateError
must be provided to handle glasses update events.
Read the javadoc for more inforamtions.
For example, in a main application class, you can initialize the SDK like this:
package com.activelook.demo;
import android.app.Application;
import com.activelook.activelooksdk.Sdk;
public class DemoApp extends Application {
private Sdk alsdk;
@Override
public void onCreate() {
super.onCreate();
this.alsdk = Sdk.init(
this.getApplicationContext(),
"MyPrivateToken",
(update) -> Log.i("GLASSES_UPDATE", "Starting glasses update."),
(update) -> { Log.i("GLASSES_UPDATE", "A glasses update is available."); return true; },
(update) -> Log.i("GLASSES_UPDATE", "Progressing glasses update."),
(update) -> Log.i("GLASSES_UPDATE", "Success glasses update."),
(update) -> Log.i("GLASSES_UPDATE", "Error glasses update.")
);
}
public Sdk getActiveLookSdk() {
return this.alsdk;
}
}
Then, use the shared singleton wherever needed. This can be called from anywhere within your application.
import com.activelook.activelooksdk.Sdk;
...
Sdk alsdk = ((DemoApp) this.getApplication()).getActiveLookSdk();
To scan for available ActiveLook glasses, simply use the
startScan(Consumer<DiscoveredGlasses> onDiscoverGlasses)
and
stopScan()
methods.
When a device is discovered, the onDiscoverGlasses
callback will be called.
You can handle these cases by providing an instance of Consumer<DiscoveredGlasses>
.
Since this is a single method interface, you can also create it on the fly with a closure.
For example, you can run some process on the UI thread:
this.alsdk.startScan(discoveredGlasses -> runOnUiThread(() -> {
Log.d("SDK", discoveredGlasses.toString());
}));
To connect to a pair of discovered glasses, use the connect(Consumer<Glasses> onConnected, Consumer<DiscoveredGlasses> onConnectionFail, Consumer<Glasses> onDisconnected)
method on the DiscoveredGlasses
object.
The connection process handle the glasses update. The registered callbacks on the SDK initialization will be triggered if a compatible update is available. Once the update process has terminated, you connection process will continue.
If the connection is successful, the onConnected.accept
method will be called and will return a Glasses
object,
which can then be used to get information about the connected ActiveLook glasses or send commands.
If the connection fails, the onConnectionFail.accept
method will be called instead.
Finally, if the connection to the glasses is lost at any point,
later, while the app is running, the onDisconnected.accept
method will be called.
Since all those handler are single method interfaces, you can create them on the fly with closures:
discoveredGlasses.connect(
glasses -> { Log.d("SDK", "Connected: " + glasses.toString()); },
discoveredGlasses -> { Log.d("SDK", "Connection fail: " + discoveredGlasses.toString()); },
glasses -> { Log.d("SDK", "Disconnected: " + discoveredGlasses.toString()); }
);
If you need to share the Glasses
object between several Activity,
and you find it hard or inconvenient to hold onto the onDisconnected
callback,
you can reset it or provide a new one by using the
setOnDisconnected(Consumer<Glasses> onDisconnected)
method on the Glasses
object:
glasses.setOnDisconnected(
glasses -> { Log.d("SDK", "New disconnected handler: " + glasses.toString()); }
);
To get information relative to discovered glasses as published over Bluetooth, you can access the following public properties:
this.alsdk.startScan(discoveredGlasses -> {
Log.d("discoveredGlasses", "Manufacturer:" + discoveredGlasses.getManufacturer());
Log.d("discoveredGlasses", "Name:" + discoveredGlasses.getName());
Log.d("discoveredGlasses", "Address:" + discoveredGlasses.getAddress());
});
Once connected, you can access more information about the device such as its firmware version, the model number etc... by using the DeviceInformation getDeviceInformation()
method:
DeviceInformation di = glasses.getDeviceInformation();
Log.d("Glasses", "ManufacturerName: " + di.getManufacturerName());
Log.d("Glasses", "ModelNumber: " + di.getModelNumber());
Log.d("Glasses", "SerialNumber: " + di.getSerialNumber());
Log.d("Glasses", "HardwareVersion: " + di.getHardwareVersion());
Log.d("Glasses", "FirmwareVersion: " + di.getFirmwareVersion());
Log.d("Glasses", "SoftwareVersion: " + di.getSoftwareVersion());
All available commands are exposed as methods in the Glasses
class.
Examples are available in the Example application.
Most commands require parameters to be sent.
// Power on the glasses
glasses.power(true);
// Set the display luminance level
glasses.luma((byte) 15)
// Draw a circle at the center of the screen
glasses.circ((short) 152, (short) 128, (byte) 50)
// Enable gesture detection sensor
glasses.gesture(true)
// Some other example
glasses.power(false);
glasses.clear();
glasses.grey((byte) 0x03);
glasses.grey((byte) 0x07);
glasses.grey((byte) 0x0B);
glasses.grey((byte) 0x0F);
glasses.demo();
glasses.test(DemoPattern.CROSS);
glasses.test(DemoPattern.FILL);
When a response is expected from the glasses, an handler must be provided as a instance or as a closure. The callback will be called asynchronously.
glasses.battery(r -> { Log.d("Battery", String.format("Battery level: %d", r)); });
It is possible to subscribe to three types of notifications that the glasses will send over Bluetooth:
- Battery level updates (periodically, every 30 seconds)
- Gesture detection sensor triggered
- Flow control events (when the state of the flow control changes)
glasses.subscribeToBatteryLevelNotifications( //Consumer<Integer> onEvent
r -> { Log.d("Notif", "Battery: " + r.toString()); }
);
glasses.subscribeToFlowControlNotifications( //Consumer<FlowControlStatus> onEvent
r -> { Log.d("Notif", "Flow control: " + r.toString()); }
);
glasses.subscribeToSensorInterfaceNotifications( //Runnable onEvent
r -> { Log.d("Notif", "Sensor: Gesture!"); }
);
When done interacting with ActiveLook glasses, simply call the disconnect()
method:
glasses.disconnect()
In order to use a DiscoveredGlasses
or a Glasses
created in one activty and consumed in another one, Intent
must be use.
These classes implements the Parcelable
interface to make it possible.
You can find an example in the sample application where the Glasses
is shared between the scanning activity and the main activity.
public class MainActivity extends AppCompatActivity {
private Glasses connectedGlasses;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
if (savedInstanceState != null) {
this.connectedGlasses = savedInstanceState.getParcelable("connectedGlasses");
}
...
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
this.connectedGlasses = data.getExtras().getParcelable("connectedGlasses");
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
if (this.connectedGlasses != null) {
savedInstanceState.putParcelable("connectedGlasses", this.connectedGlasses);
}
}
...
}
public class ScanningActivity extends AppCompatActivity {
...
device.connect(glasses -> {
Intent returnIntent = new Intent();
returnIntent.putExtra("connectedGlasses", glasses);
ScanningActivity.this.setResult(Activity.RESULT_FIRST_USER, returnIntent);
ScanningActivity.this.finish();
}, null, null);
...
}
For now, it is unclear if Android Wear can be supported. A study on the compatibility and limits needs to be done and this sample code will be updated accordingly.
Compiled and validated with Android studio 3.1.3 for Windows 64-bit Using (automatically installed):
- Gradle 4.4
- Android SDK Platform 21
- Build Tools revision 27.0.3
This code is licensed under the MIT Licence. You may obtain a copy of the License at
https://opensource.org/licenses/MIT
MDMBLE application embeds a SQLite database to store internal settings The unique MessageBLE(int id, String message, String alias) table stores the commands:
- id: incremental number
- message: command to be sent
- alias: text displayed on the interface
Caution: the database is cleared only when uninstalling the application.