headwind_plugin 0.2.1 copy "headwind_plugin: ^0.2.1" to clipboard
headwind_plugin: ^0.2.1 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. The Headwind MDM SDK — the on-device binding to the com.hmdm.launcher app already installed on managed devices. This is the primary source because it performs a live launcher read.
  2. Android RestrictionsManager — standard managed configuration / app restrictions delivered by an EMM or DPC. Used as a fallback for any attribute the SDK does not provide, so non-Headwind EMMs still work.

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) The Headwind SDK, then RestrictionsManager for attributes the SDK 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 binds the Headwind SDK with the application context, reads RestrictionsManager as a fallback (re-reading on the system restrictions-changed broadcast), 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 SDK + RestrictionsManager 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 as a fallback, using the same keys passed to HeadwindMdm, when the Headwind SDK does not provide a value:

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
SDK returns a non-empty value Use the Headwind SDK value.
SDK omits the value and a restriction key exists Use the RestrictionsManager 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 can serve config: the SDK connects, or the restrictions bundle is non-empty. 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.

Example app #

An Android example app is available in example/. It watches api_base_url, app_color, and route_auto_sync_time, then shows connection state, managed-device state, device id, source mode, and the latest config snapshot.

cd example
flutter run

For a managed-device test, configure those keys for package com.example.headwind_plugin_example in Headwind MDM or another DPC that supports Android app restrictions.

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