headwind_plugin 0.2.0 copy "headwind_plugin: ^0.2.0" to clipboard
headwind_plugin: ^0.2.0 copied to clipboard

PlatformAndroid

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 on Android and streams it to Dart, from two sources:

  1. Android RestrictionsManager — standard managed configuration / app restrictions delivered by an EMM or DPC (Headwind MDM included, when it pushes app restrictions). This is the primary source.
  2. The Headwind MDM SDK — the on-device binding to the com.hmdm.launcher app already installed on managed devices. Used as a fallback for any attribute RestrictionsManager does not provide, or — via the preferSdk toggle — as the sole source (this plugin's original behaviour).

Both sources are Android-only. On any other platform the plugin degrades cleanly to "not connected / no values".

How attributes resolve #

preferSdk Source order
false (default) RestrictionsManager, then the SDK for attributes it omits.
true The Headwind SDK only; RestrictionsManager is bypassed.

What's inside #

  • Dart HeadwindMdm — connect, refresh, read attributes, toggle preferSdk, and a snapshots stream of {attr: value} maps.
  • Android HeadwindPlugin (Kotlin) — a pure FlutterPlugin that reads RestrictionsManager (re-reading on the system restrictions-changed broadcast), binds the Headwind SDK with the application context, and pushes snapshots back to Dart. No custom Activity required in the host app.
  • The Headwind SDK AAR, bundled as a local Maven artifact under android/libs/maven/com/hmdm/hmdm/1.1.8/, and the <queries><package android:name="com.hmdm.launcher"/></queries> manifest entry.

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-Android devices

To read exclusively from the Headwind SDK, pass preferSdk: true at construction, or flip it at runtime:

final mdm = HeadwindMdm(const ['app_color'], preferSdk: true);
// ...or later:
await mdm.setPreferSdk(true);  // bypass RestrictionsManager
await mdm.setPreferSdk(false); // back to RestrictionsManager + SDK fallback

Each setPreferSdk pushes a fresh snapshot reflecting the new source order.

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, preferSdk = false}) Watches attrs.
Stream<Map<String,String>> snapshots Full snapshot on connect and on change.
Future<bool> connect() Bind sources; emits the initial snapshot.
Future<bool> refresh() Ask the launcher to re-sync the server, then re-read.
Future<bool> setPreferSdk(bool) Toggle SDK-only vs RestrictionsManager+SDK; re-emits.
bool preferSdk Whether config is read from the SDK only.
Future<bool> isManaged() Whether the device is managed (SDK or restrictions).
Future<String?> getDeviceId() Headwind device id, if available.
bool isConnected Whether the last connect() succeeded.
Future<void> dispose() Release observer, channel handler and stream.

Android RestrictionsManager #

RestrictionsManager is Android's standard managed app configuration API. When an EMM, DPC, or Headwind MDM pushes app restrictions for the host app package, Android exposes them to the app as an applicationRestrictions bundle.

This plugin reads that bundle first, using the same keys passed to HeadwindMdm:

final mdm = HeadwindMdm(const [
  'api_base_url',
  'app_color',
]);

For the example above, the Android side looks for restrictions named exactly api_base_url, app_color. Values are returned to Dart in the regular snapshots stream:

mdm.snapshots.listen((values) {
  final baseUrl = values['api_base_url'];
});

Resolution is per attribute:

Situation Result
Restriction key exists and has a non-empty value Use the RestrictionsManager value.
Restriction key is absent or empty Fall back to the Headwind SDK value.
preferSdk: true Skip RestrictionsManager and read only from the SDK.

No host Activity changes are required. The native plugin reads restrictions with the application context and registers for ACTION_APPLICATION_RESTRICTIONS_CHANGED, so pushed changes are re-read and emitted without app restart. refresh() also emits a fresh snapshot immediately because restrictions are local and synchronous, even if the Headwind SDK is not connected.

connect() returns true on Android when either source is usable: the SDK connects, or RestrictionsManager can be read. isManaged() is also true when the SDK reports a managed device or when the restrictions bundle is non-empty.

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 bundled as a local Maven artifact under android/libs/maven/com/hmdm/hmdm/1.1.8/ and referenced as com.hmdm:hmdm:1.1.8 from the plugin Gradle file. Because the plugin is consumed as a source subproject, it should land on the host app's runtime classpath automatically.

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/maven/com/hmdm/hmdm/1.1.8/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() returns false (never throws) on non-Android platforms. On Android it reports true whenever a source can serve config — the SDK bound, or RestrictionsManager reachable (unless preferSdk is set, which requires the SDK).
  • RestrictionsManager values refresh on the system ACTION_APPLICATION_RESTRICTIONS_CHANGED broadcast; no polling needed.
  • 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".
0
likes
150
points
49
downloads

Documentation

API reference

Publisher

verified publisheryanuar.id

Weekly Downloads

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.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on headwind_plugin

Packages that implement headwind_plugin