Coder Social home page Coder Social logo

android-sdk's Introduction

ActiveLookSDK

Requirements

In order to use the ActiveLook SDK for android, you should have Android Studio installed.

License

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.

Quick start

You can start displaying custom data in your glasses in less than 10 minutes. Ready?

Requirements

You will need the following:

  • A pair of glasses
  • AndroidStudio up
  • An android device with BLE

Step 1: Start a new Android project

Open AndroidStudio and create a new project.

Step 2: Add the ActiveLook SDK dependency

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.

  1. For the build.gradle file, you should add:
allprojects {
  repositories {
    ...
    maven { url 'https://jitpack.io' }
  }
}
  1. 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:4.5.5'
}

Step 3: Set up your android device

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.

Step 4: Update the application source code

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,
            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

Alternative getting started using demo app

You can check our demo application on https://github.com/ActiveLook/demo-app


More in depth example

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.

Initialization

To start using the SDK, first import and initialize the sdk. 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(),
      (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();

Scanning

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());
}));

Connect to ActiveLook glasses

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()); }
);

Device information

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());

Commands

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)); });

Notifications

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!"); }
);

Disconnect

When done interacting with ActiveLook glasses, simply call the disconnect() method:

glasses.disconnect()

Sharing glasses across multiple activities

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);
...
}

About Android Wear

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.

About the project

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.

android-sdk's People

Contributors

activelook-dev avatar kiiks avatar ondrap avatar sylvainromillon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

android-sdk's Issues

Coult not write rx - glasses crash

Hi there,

I am experiencing a strange issue. Sometimes after disconnecting and reconnecting to the glasses without turning them off in between, the following error message is logged to Logcat thousands of times, eventually followed by a StackOverflowException. Unfortunately I cannot reliably reproduce the error, sometimes it happens, sometimes it doesn't.

The biggest issue with this is that it crashes the glasses to the point where I am unable to turn them off by long pressing the power button. I tried holding the power button for a very long time (~10s) and eventually the glasses turned off but refused to turn back on. They can be reset by connecting them to the charging cable after which everything starts working normally.

Package name is replaced by xxxxxxxxx due to an NDA and for privacy reasons.

2024-01-11 10:21:49.388 26838-26906 unstackWri...acteristic de....xxxxxxxxxxxxxx  E  Error, retry
2024-01-11 10:21:49.396 26838-26906 unstackWri...risticLoop de....xxxxxxxxxxxxxx  E  Could not write rx: FF37011C4D00C8006404FFFF4E6F206461746120736F7572636500AA
FATAL EXCEPTION: Thread-8
Process: xxxxxxxxxx, PID: 27035
java.lang.StackOverflowError: stack size 1039KB
	at java.util.ArrayList$Itr.<init>(ArrayList.java:974)
	at java.util.ArrayList.iterator(ArrayList.java:954)
	at com.activelook.activelooksdk.core.ble.GlassesGattCallbackImpl.lambda$unstackWriteRxCharacteristicLoop$4(GlassesGattCallbackImpl.java:399)
	at com.activelook.activelooksdk.core.ble.GlassesGattCallbackImpl$$ExternalSyntheticLambda2.accept(Unknown Source:4)
	at com.activelook.activelooksdk.core.ble.GlassesGattCallbackImpl.lambda$unstackWriteRxCharacteristicLoop$3(GlassesGattCallbackImpl.java:369)
	at com.activelook.activelooksdk.core.ble.GlassesGattCallbackImpl$$ExternalSyntheticLambda1.run(Unknown Source:2)
	at com.activelook.activelooksdk.core.ble.GlassesGattCallbackImpl.lambda$unstackWriteRxCharacteristicLoop$4(GlassesGattCallbackImpl.java:399)
	at com.activelook.activelooksdk.core.ble.GlassesGattCallbackImpl$$ExternalSyntheticLambda2.accept(Unknown Source:4)
	at com.activelook.activelooksdk.core.ble.GlassesGattCallbackImpl.lambda$unstackWriteRxCharacteristicLoop$3(GlassesGattCallbackImpl.java:369)
	at com.activelook.activelooksdk.core.ble.GlassesGattCallbackImpl$$ExternalSyntheticLambda1.run(Unknown Source:2)
	at com.activelook.activelooksdk.core.ble.GlassesGattCallbackImpl.lambda$unstackWriteRxCharacteristicLoop$4(GlassesGattCallbackImpl.java:399)
	at com.activelook.activelooksdk.core.ble.GlassesGattCallbackImpl$$ExternalSyntheticLambda2.accept(Unknown Source:4)

Connect to a device by specifying an address

Would it be possible to connect directly to a device by specifing an address? Currently the API allows this only by first running scan; however when the app already has an address given, just connecting to the device looks like a better workflow and one doesn't have to synchronize with other parts of the application trying to scan for BLE devices.

I'm not a BlueTooth android expert, but the API seems to allow for this:
https://developer.android.com/reference/android/bluetooth/BluetoothAdapter#getRemoteDevice(java.lang.String)

token

Hello,
I am trying to connect to my new glasses DK1 using the sample code.
when I run it it, the code appears to initialise and I get confirmation in the log.e below

2022-12-29 11:00:37.977 32675-32675/com.example.githubexample E/ANDROID: Permissions
2022-12-29 11:00:37.990 32675-32675/com.example.githubexample E/SDK: Init
2022-12-29 11:00:37.991 32675-32675/com.example.githubexample E/SDK: Instance
2022-12-29 11:00:37.991 32675-32675/com.example.githubexample E/SDK: Scan
2022-12-29 11:00:38.446 32675-32675/com.example.githubexample E/DISCOVER: Glasses connecting: 80:15:05:00:06:0A
2022-12-29 11:00:41.284 32675-379/com.example.githubexample E/Volley: [34533] NetworkUtility.shouldRetryException: Unexpected response code 403 for http://vps468290.ovh.net/v1/firmwares/ALK02A/your-sdk-token-provided-by-microoled?compatibility=4&min-version=4.6.0
2022-12-29 11:00:41.287 32675-32675/com.example.githubexample W/System.err: com.android.volley.AuthFailureError
2022-12-29 11:00:41.291 32675-32675/com.example.githubexample E/ERROR: Glasses could not be connected
2022-12-29 11:00:42.156 32675-381/com.example.githubexample E/Volley: [34534] NetworkUtility.shouldRetryException: Unexpected response code 403 for http://vps468290.ovh.net/v1/firmwares/ALK01A/your-sdk-token-provided-by-microoled?compatibility=4&min-version=4.6.0

in the documentation it suggest I need a TOKEN

"_Initialization
To start using the SDK, first import and initialize the sdk. You will need a token provided by Microoled to authenticate."

any assistance would be appreciated
regards
Jeremy

Flow control problems

There seems to be something not quite correct with the way the device is handling flow control. I'm quite deterministically able to induce the following situations:

  • run 6 fontSave (for 6 fonts) interspersed with [color, rect, rectf] (progressbar).
    • when there is no progressbar drawing, everything is OK
    • when there is progressbar drawing, the font saving randomly fails
    • when I introduce a small (200ms) delay after each progressbar update, it works OK
  • run a bunch of mixed imgSave, layoutSave, layoutDisplayExtended (the order is such that the images and layouts needed for the display are correctly saved) is able to get the glasses in some semi-stuck mode - screen is blank, it doesn't react to e.g. BT disconnection. Introducing small delay before or after each imgSave and layoutSave seems to fix the problem.

It seems to me there is some dependency between 'drawing' and 'saving'. and one doesn't like the other.

Also, is there some way to wait for the send buffer to get empty? It may happen that I get too many commands in queue and instead of building an ever larger backlog, I'd be able to start dropping the display requests in the application. I'd need some way to call flush which would return when the sending queue is empty. Currently I just read some battery status and assume that when I get a value back, everything sent so far was processed. I just wonder if there was some way without the extra roundtrip.

International support?

Hi
I am a Japanese engineer.

I bought development kit DK-1 and tried running the sample program.
I noticed that Japanese is not displayed.
Do you have any plans for internationalization or Japanese language?

Thanks

Error while sending datas to glasses

I tried to set me up a really basic project but the problem is that I can't send data to the glasses

Thr following error occures on any "glasses.method" such as circ, clear or txt

[ 0xFF, 0x01, 0x01, 0x06, 0x00, 0xAA ]
2023-12-19 12:13:22.499 17209-17225 writeCommand com.example.testactivelook W payload length: 6
2023-12-19 12:13:22.523 17209-17225 BluetoothGatt com.example.testactivelook W Unhandled exception in callback
java.lang.ArrayIndexOutOfBoundsException: src.length=6 srcPos=0 dst.length=6 dstPos=0 length=256
at java.lang.System.arraycopy(Native Method)
at com.activelook.activelooksdk.core.ble.GlassesGattCallbackImpl.unstackWriteCommand(GlassesGattCallbackImpl.java:282)
at com.activelook.activelooksdk.core.ble.GlassesGattCallbackImpl.writeCommand(GlassesGattCallbackImpl.java:260)
at com.activelook.activelooksdk.core.ble.GlassesImpl.writeCommand(GlassesImpl.java:70)
at com.activelook.activelooksdk.core.GlassesCommandsAdapter.write(GlassesCommandsAdapter.java:200)
at com.activelook.activelooksdk.core.GlassesCommandsAdapter.clear(GlassesCommandsAdapter.java:228)
at com.example.testactivelook.MainActivity.lambda$onCreate$0(MainActivity.java:54)
at com.example.testactivelook.MainActivity$$ExternalSyntheticLambda2.accept(Unknown Source:2)
at com.activelook.activelooksdk.core.ble.GlassesGattCallbackImpl.onCharacteristicRead(GlassesGattCallbackImpl.java:139)
at android.bluetooth.BluetoothGattCallback.onCharacteristicRead(BluetoothGattCallback.java:108)
at android.bluetooth.BluetoothGatt$1$6.run(BluetoothGatt.java:425)
at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:864)
at android.bluetooth.BluetoothGatt.-$$Nest$mrunOrQueueCallback(Unknown Source:0)
at android.bluetooth.BluetoothGatt$1.onCharacteristicRead(BluetoothGatt.java:419)
at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:182)
at android.os.Binder.execTransactInternal(Binder.java:1290)
at android.os.Binder.execTransact(Binder.java:1249)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.