headwind_plugin 0.1.0
headwind_plugin: ^0.1.0 copied to clipboard
Flutter plugin that bridges Headwind MDM (h-mdm.com) per-device managed configuration to Dart. Android-only: binds to the on-device com.hmdm.launcher and streams config snapshots.
headwind_plugin #
A small, project-agnostic Flutter plugin that reads per-device managed configuration from Headwind MDM and streams it to Dart.
Headwind MDM is Android-only. Rather than calling the Headwind server REST
API, the app rides on the on-device SDK that binds to the com.hmdm.launcher
app already installed on managed devices. On any other platform — or on an
Android device without the launcher — the plugin degrades cleanly to
"not connected / no values".
What's inside #
- Dart
HeadwindMdm— connect, refresh, read attributes, and asnapshotsstream of{attr: value}maps. - Android
HeadwindPlugin(Kotlin) — a pureFlutterPluginthat binds the Headwind SDK with the application context and pushes snapshots back to Dart. No customActivityrequired in the host app. - The Headwind SDK AAR (
android/libs/hmdm-1.1.8.aar) and the<queries><package android:name="com.hmdm.launcher"/></queries>manifest entry, both bundled so consumers don't have to wire them.
Usage #
import 'package:headwind_plugin/headwind_plugin.dart';
final mdm = HeadwindMdm(const [
'api_base_url',
'app_color',
]);
mdm.snapshots.listen((values) {
// e.g. {'api_base_url': 'http://localhost', 'app_color': '#4A6F00'}
});
final connected = await mdm.connect(); // false on non-managed devices
By default the plugin re-syncs every time the app returns to the foreground
(refreshOnResume: true), so values follow the MDM dashboard without an app
restart or a manual "update configuration" tap. Pass refreshOnResume: false
to opt out and drive refresh() yourself.
API #
| Member | Description |
|---|---|
HeadwindMdm(attrs, {refreshOnResume = true}) |
Watches attrs. |
Stream<Map<String,String>> snapshots |
Full snapshot on connect and on change. |
Future<bool> connect() |
Bind to the launcher; emits the initial snapshot. |
Future<bool> refresh() |
Ask the launcher to re-sync the server, then re-read. |
Future<bool> isManaged() |
Whether the device is under Headwind management. |
Future<String?> getDeviceId() |
Headwind device id, if available. |
bool isConnected |
Whether the last connect() succeeded. |
Future<void> dispose() |
Release observer, channel handler and stream. |
Consuming from another project #
Add it as a path (or git) dependency:
dependencies:
headwind_plugin:
path: ../headwind_plugin
Then flutter pub get. The plugin auto-registers — no changes to your
MainActivity or AndroidManifest.xml are needed.
Runtime note. The SDK is a local
.aar. It is bundled in the plugin and, because the plugin is consumed as a source subproject, ships to the host app's runtime classpath automatically. If a host project's Gradle setup does not propagate it (you would see aClassNotFoundException: com.hmdm.HeadwindMDMat runtime), copyandroid/libs/hmdm-1.1.8.aarinto the host'sandroid/app/libs/and addimplementation files('libs/hmdm-1.1.8.aar')to itsapp/build.gradle.
License #
The headwind_plugin source code is licensed under the MIT License.
Third-party software #
This plugin bundles the Headwind MDM Android SDK
(android/libs/hmdm-1.1.8.aar), which is
licensed separately under the
Apache License, Version 2.0.
Headwind MDM: Open Source Android Mobile Device Management Software Project website: https://h-mdm.com Source: https://github.com/h-mdm/hmdm-android (c) 2019 Headwind Solutions LLC (http://www.h-sms.com)
Its license and required attribution notice live next to the .aar in
android/libs/ — see
NOTICE and
LICENSE-APACHE-2.0. Anyone who
redistributes this plugin (or the bundled .aar) must keep those two files
intact.
Notes / gotchas #
connect()returnsfalse(never throws) when the launcher is absent.- The launcher's config-changed callback is not reliable for third-party
apps, so
refresh()re-reads on a short schedule (≈0.8–6 s) after triggering a server sync. Duplicate snapshots are cheap; listeners can diff them. - Attributes left blank in the Headwind dashboard arrive as empty strings — callers decide whether to treat blank as "absent".