Skip to content

Commit

Permalink
feat: add Bluetooth Low Energy plugin (#802)
Browse files Browse the repository at this point in the history
* feat: add Bluetooth Low Energy plugin

* wip

* wip
  • Loading branch information
robingenz authored May 15, 2024
1 parent b8dc2c3 commit 3ab4124
Show file tree
Hide file tree
Showing 27 changed files with 655 additions and 33 deletions.
1 change: 1 addition & 0 deletions android/app/capacitor.build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ dependencies {
implementation project(':capacitor-share')
implementation project(':capawesome-team-capacitor-android-battery-optimization')
implementation project(':capawesome-team-capacitor-android-foreground-service')
implementation project(':capawesome-team-capacitor-bluetooth-low-energy')
implementation project(':capawesome-team-capacitor-datetime-picker')
implementation project(':capawesome-team-capacitor-file-compressor')
implementation project(':capawesome-team-capacitor-file-opener')
Expand Down
11 changes: 9 additions & 2 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@

<receiver android:name="io.capawesome.capacitorjs.plugins.foregroundservice.NotificationActionBroadcastReceiver" />
<service android:name="io.capawesome.capacitorjs.plugins.foregroundservice.AndroidForegroundService" />

<service android:name="io.capawesome.capacitorjs.plugins.bluetoothle.BluetoothLowEnergyService" android:foregroundServiceType="connectedDevice" />
</application>

<uses-feature android:name="android.hardware.camera" android:required="false" />
Expand Down Expand Up @@ -79,7 +81,12 @@
<!-- Battery Optimization -->
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<!-- Foreground Service -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<!-- Foreground Service + Bluetooth Low Energy -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<!-- Bluetooth Low Energy -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
</manifest>
4 changes: 4 additions & 0 deletions android/app/src/main/assets/capacitor.plugins.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
"pkg": "@capawesome-team/capacitor-android-foreground-service",
"classpath": "io.capawesome.capacitorjs.plugins.foregroundservice.ForegroundServicePlugin"
},
{
"pkg": "@capawesome-team/capacitor-bluetooth-low-energy",
"classpath": "io.capawesome.capacitorjs.plugins.bluetoothle.BluetoothLowEnergyPlugin"
},
{
"pkg": "@capawesome-team/capacitor-datetime-picker",
"classpath": "io.capawesome.capacitorjs.plugins.datetimepicker.DatetimePickerPlugin"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dev.robingenz.capacitor.plugindemo;

import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;

import androidx.annotation.NonNull;

import com.getcapacitor.Logger;

public class BluetoothLowEnergyHeadlessTask {
public void onCharacteristicChanged(@NonNull BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value) {
Logger.debug("BluetoothLowEnergyHeadlessTask", "Characteristic changed: " + characteristic.getUuid().toString() + " Value: " + value);
}
}
3 changes: 3 additions & 0 deletions android/capacitor.settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ project(':capawesome-team-capacitor-android-battery-optimization').projectDir =
include ':capawesome-team-capacitor-android-foreground-service'
project(':capawesome-team-capacitor-android-foreground-service').projectDir = new File('../node_modules/@capawesome-team/capacitor-android-foreground-service/android')

include ':capawesome-team-capacitor-bluetooth-low-energy'
project(':capawesome-team-capacitor-bluetooth-low-energy').projectDir = new File('../node_modules/@capawesome-team/capacitor-bluetooth-low-energy/android')

include ':capawesome-team-capacitor-datetime-picker'
project(':capawesome-team-capacitor-datetime-picker').projectDir = new File('../node_modules/@capawesome-team/capacitor-datetime-picker/android')

Expand Down
2 changes: 2 additions & 0 deletions ios/App/App/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
<string>Store camera photos to camera</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>To Pick Photos from Library</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>The app needs access to Bluetooth peripherals to communicate with Bluetooth devices.</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
Expand Down
2 changes: 2 additions & 0 deletions ios/App/App/capacitor.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
"HapticsPlugin",
"KeyboardPlugin",
"SharePlugin",
"BluetoothLowEnergyPlugin",
"BluetoothLowEnergyPlugin",
"DatetimePickerPlugin",
"DatetimePickerPlugin",
"FileCompressorPlugin",
Expand Down
1 change: 1 addition & 0 deletions ios/App/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def capacitor_pods
pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
pod 'CapacitorShare', :path => '../../node_modules/@capacitor/share'
pod 'CapawesomeTeamCapacitorBluetoothLowEnergy', :path => '../../node_modules/@capawesome-team/capacitor-bluetooth-low-energy'
pod 'CapawesomeTeamCapacitorDatetimePicker', :path => '../../node_modules/@capawesome-team/capacitor-datetime-picker'
pod 'CapawesomeTeamCapacitorFileCompressor', :path => '../../node_modules/@capawesome-team/capacitor-file-compressor'
pod 'CapawesomeTeamCapacitorFileOpener', :path => '../../node_modules/@capawesome-team/capacitor-file-opener'
Expand Down
8 changes: 7 additions & 1 deletion ios/App/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ PODS:
- Capacitor
- CapawesomeCapacitorScreenOrientation (6.0.0):
- Capacitor
- CapawesomeTeamCapacitorBluetoothLowEnergy (6.0.0):
- Capacitor
- CapawesomeTeamCapacitorDatetimePicker (6.0.0):
- Capacitor
- CapawesomeTeamCapacitorFileCompressor (6.0.0):
Expand Down Expand Up @@ -79,6 +81,7 @@ DEPENDENCIES:
- "CapawesomeCapacitorManagedConfigurations (from `../../node_modules/@capawesome/capacitor-managed-configurations`)"
- "CapawesomeCapacitorPhotoEditor (from `../../node_modules/@capawesome/capacitor-photo-editor`)"
- "CapawesomeCapacitorScreenOrientation (from `../../node_modules/@capawesome/capacitor-screen-orientation`)"
- "CapawesomeTeamCapacitorBluetoothLowEnergy (from `../../node_modules/@capawesome-team/capacitor-bluetooth-low-energy`)"
- "CapawesomeTeamCapacitorDatetimePicker (from `../../node_modules/@capawesome-team/capacitor-datetime-picker`)"
- "CapawesomeTeamCapacitorFileCompressor (from `../../node_modules/@capawesome-team/capacitor-file-compressor`)"
- "CapawesomeTeamCapacitorFileOpener (from `../../node_modules/@capawesome-team/capacitor-file-opener`)"
Expand Down Expand Up @@ -132,6 +135,8 @@ EXTERNAL SOURCES:
:path: "../../node_modules/@capawesome/capacitor-photo-editor"
CapawesomeCapacitorScreenOrientation:
:path: "../../node_modules/@capawesome/capacitor-screen-orientation"
CapawesomeTeamCapacitorBluetoothLowEnergy:
:path: "../../node_modules/@capawesome-team/capacitor-bluetooth-low-energy"
CapawesomeTeamCapacitorDatetimePicker:
:path: "../../node_modules/@capawesome-team/capacitor-datetime-picker"
CapawesomeTeamCapacitorFileCompressor:
Expand Down Expand Up @@ -165,6 +170,7 @@ SPEC CHECKSUMS:
CapawesomeCapacitorManagedConfigurations: e6dbcbcf45d7b1f7acfa9b54a02e15201604a803
CapawesomeCapacitorPhotoEditor: 231e03215f026e0c2eeb6de0910e095e4aa0c823
CapawesomeCapacitorScreenOrientation: 82675c41cb616a64630c834b06344515a2849ff3
CapawesomeTeamCapacitorBluetoothLowEnergy: 8a79dcc9f2edde2cf8304b4f628fa3260e12a5d4
CapawesomeTeamCapacitorDatetimePicker: 6dd2e7ae30c4b55afd48f19bfeb4578dcaa9263e
CapawesomeTeamCapacitorFileCompressor: be4a55e6476c4084ef370e84c224fc63db27f7a5
CapawesomeTeamCapacitorFileOpener: b406c1a389cbb4b1a3e9d686c34c9a357abb5902
Expand All @@ -173,6 +179,6 @@ SPEC CHECKSUMS:
Cloudinary: 1e8ed5ce1418ba50df625c1fec80ebec746141b6
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef

PODFILE CHECKSUM: 4ca49903ac471928dcd577796e3d0c22498439f5
PODFILE CHECKSUM: f974117138a426727b8e09e678d6f634d232fac0

COCOAPODS: 1.15.2
48 changes: 48 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@capacitor/share": "6.0.0",
"@capawesome-team/capacitor-android-battery-optimization": "6.0.0",
"@capawesome-team/capacitor-android-foreground-service": "6.0.0",
"@capawesome-team/capacitor-bluetooth-low-energy": "6.0.0",
"@capawesome-team/capacitor-datetime-picker": "6.0.0",
"@capawesome-team/capacitor-file-compressor": "6.0.0",
"@capawesome-team/capacitor-file-opener": "6.0.0",
Expand Down
7 changes: 7 additions & 0 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ const routes: Routes = [
loadChildren: () =>
import('./modules/badge/badge.module').then(m => m.BadgePageModule),
},
{
path: 'bluetooth-low-energy',
loadChildren: () =>
import('./modules/bluetooth-low-energy/bluetooth-low-energy.module').then(
m => m.BluetoothLowEnergyPageModule,
),
},
{
path: 'cloudinary',
loadChildren: () =>
Expand Down
8 changes: 8 additions & 0 deletions src/app/core/services/dialog/dialog.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ export class DialogService {
private popoverCtrl: PopoverController,
) {}

public async dismissModal(
data?: any,
role?: string,
id?: string,
): Promise<boolean> {
return this.modalCtrl.dismiss(data, role, id);
}

public async showAlert(opts?: AlertOptions): Promise<HTMLIonAlertElement> {
const alert = await this.alertCtrl.create(opts);
await alert.present();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core';
import { Component, NgZone, OnInit } from '@angular/core';
import { ForegroundService } from '@capawesome-team/capacitor-android-foreground-service';

@Component({
Expand All @@ -12,15 +12,10 @@ export class AndroidForegroundServicePage implements OnInit {
private readonly GH_URL =
'https://github.com/capawesome-team/capacitor-plugins';

constructor() {}
constructor(private readonly ngZone: NgZone) {}

public ngOnInit(): void {
ForegroundService.removeAllListeners().then(() => {
ForegroundService.addListener('buttonClicked', event => {
ForegroundService.stopForegroundService();
ForegroundService.moveToForeground();
});
});
this.addListeners();
}

public async requestPermissions(): Promise<void> {
Expand Down Expand Up @@ -53,4 +48,15 @@ export class AndroidForegroundServicePage implements OnInit {
public openOnGithub(): void {
window.open(this.GH_URL, '_blank');
}

private addListeners(): void {
ForegroundService.removeAllListeners().then(() => {
ForegroundService.addListener('buttonClicked', event => {
this.ngZone.run(() => {
ForegroundService.stopForegroundService();
ForegroundService.moveToForeground();
});
});
});
}
}
34 changes: 20 additions & 14 deletions src/app/modules/background-task/background-task.page.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Component, NgZone, OnDestroy, OnInit } from '@angular/core';
import { App } from '@capacitor/app';
import { PluginListenerHandle } from '@capacitor/core';
import { BackgroundTask } from '@capawesome/capacitor-background-task';
Expand All @@ -13,21 +13,10 @@ export class BackgroundTaskPage implements OnInit, OnDestroy {
'https://github.com/capawesome-team/capacitor-plugins';
private appStateChangeListener: Promise<PluginListenerHandle> | undefined;

constructor() {}
constructor(private readonly ngZone: NgZone) {}

public ngOnInit() {
this.appStateChangeListener = App.addListener(
'appStateChange',
async ({ isActive }) => {
if (isActive) {
return;
}
const taskId = await BackgroundTask.beforeExit(async () => {
await this.runTask();
BackgroundTask.finish({ taskId });
});
},
);
this.addListeners();
}

public ngOnDestroy() {
Expand All @@ -38,6 +27,23 @@ export class BackgroundTaskPage implements OnInit, OnDestroy {
window.open(this.GH_URL, '_blank');
}

private addListeners(): void {
this.appStateChangeListener = App.addListener(
'appStateChange',
({ isActive }) => {
this.ngZone.run(async () => {
if (isActive) {
return;
}
const taskId = await BackgroundTask.beforeExit(async () => {
await this.runTask();
BackgroundTask.finish({ taskId });
});
});
},
);
}

private async runTask(): Promise<void> {
const taskDurationMs = 120000;
const end = new Date().getTime() + taskDurationMs;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<ion-header>
<ion-toolbar>
<ion-title>Device</ion-title>
<ion-buttons slot="end">
<ion-button (click)="closeModal()">
<ion-icon name="close"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>

<ion-content>
<ion-card>
<ion-card-header>
<ion-card-title>{{ device?.name || device?.id }}</ion-card-title>
</ion-card-header>
<ion-card-content>
<ion-button expand="block" (click)="connect()">Connect</ion-button>
<ion-button expand="block" (click)="createBond()">Create Bond</ion-button>
<ion-button expand="block" (click)="disconnect()">Disconnect</ion-button>
<ion-button expand="block" (click)="discoverServices()"
>Discover Service</ion-button
>
<ion-button expand="block" (click)="getServices()"
>Get Services</ion-button
>
<ion-button expand="block" (click)="isBonded()">Is Bonded</ion-button>
<ion-button expand="block" (click)="readCharacteristic()"
>Read Characteristic</ion-button
>
<ion-button expand="block" (click)="readRssi()">Read Rssi</ion-button>
<ion-button expand="block" (click)="requestConnectionPriority()"
>Request Connection Priority</ion-button
>
<ion-button expand="block" (click)="requestMtu()">Request Mtu</ion-button>
<ion-button expand="block" (click)="startCharacteristicNotifications()"
>Start Characteristic Notifications</ion-button
>
<ion-button expand="block" (click)="stopCharacteristicNotifications()"
>Stop Characteristic Notifications</ion-button
>
<ion-button expand="block" (click)="writeCharacteristic()"
>Write Characteristic</ion-button
>
</ion-card-content>
</ion-card>
</ion-content>
Loading

0 comments on commit 3ab4124

Please sign in to comment.