由于flutter插件市场里面的pedometer没有支持鸿蒙,并且没有统一接口,所以我重写了整个工程,实现统一的interface,并且兼容pedometer包里面的所有接口和使用方式,并在此基础上增加了对鸿蒙的支持。
先看下整体的代码工程和接口
接口层设计
核心代码
一,接口部分
1,pedometer_interface.dart
import 'dart:async';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'method_channel_pedometer.dart';
import 'pedestrian_status.dart';
import "step_count.dart";
export "pedestrian_status.dart";
export "step_count.dart";
abstract class PedometerPlatform extends PlatformInterface {
/// Default constructor for [PedometerPlatform]
PedometerPlatform() : super(token: _token);
/// The token which [PlatformInterface.verifyToken] needs to be verify
static final Object _token = Object();
/// Private instance which will be only create once
static MethodChannelPedometer _instance = MethodChannelPedometer();
/// The default instance of [ScreenBrightnessPlatform] to use.
///
/// Defaults to [MethodChannelScreenBrightness].
static MethodChannelPedometer get instance => _instance;
/// Platform-specific plugins should set this with their own platform-specific
/// class that extends [ScreenBrightnessPlatform] when they register themselves.
static set instance(MethodChannelPedometer instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
/// Returns one step at a time.
/// Events come every time a step is detected.
Stream<PedestrianStatus> get pedestrianStatusStream {
throw UnimplementedError(
'setApplicationScreenBrightness(brightness) has not been implemented.');
}
/// Returns the steps taken since last system boot.
/// Events may come with a delay.
Stream<StepCount> get stepCountStream {
throw UnimplementedError(
'setApplicationScreenBrightness(brightness) has not been implemented.');
}
}
2,method_channel_pedometer.dart
import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'pedometer_interface.dart';
import 'pedestrian_status.dart';
import 'step_count.dart';
import 'constant.dart';
/// Implementation of screen brightness platform interface
class MethodChannelPedometer extends PedometerPlatform {
static const EventChannel _stepDetectionChannel =
const EventChannel('step_detection');
static const EventChannel _stepCountChannel =
const EventChannel('step_count');
static StreamController<PedestrianStatus> _androidPedestrianController =
StreamController.broadcast();
/// Returns one step at a time.
/// Events come every time a step is detected.
@override
Stream<PedestrianStatus> get pedestrianStatusStream {
Stream<PedestrianStatus> stream = _stepDetectionChannel
.receiveBroadcastStream()
.map((event) => PedestrianStatus.fromEvent(event));
if (Platform.isAndroid) return _androidStream(stream);
return stream;
}
/// Transformed stream for the Android platform
static Stream<PedestrianStatus> _androidStream(
Stream<PedestrianStatus> stream) {
/// Init a timer and a status
Timer? t;
int? pedestrianStatus;
/// Listen for events on the original stream
/// Transform these events by using the timer
stream.listen((dynamic e) {
/// If an event is received it means the status is 'walking'
/// If the timer has been started, it should be cancelled
/// to prevent sending out additional 'walking' events
if (t != null) {
t!.cancel();
/// If a previous status was either not set yet, or was 'stopped'
/// then a 'walking' event should be emitted.
if (pedestrianStatus == null || pedestrianStatus == Constant.stopped) {
_androidPedestrianController.add(PedestrianStatus.fromEvent(Constant.walking));
pedestrianStatus = Constant.walking;
}
}
/// After receiving an event, start a timer for 2 seconds, after
/// which a 'stopped' event is emitted. If it manages to go through,
/// it is because no events were received for the 2 second duration
t = Timer(Duration(seconds: 2), () {
_androidPedestrianController.add(PedestrianStatus.fromEvent(Constant.stopped));
pedestrianStatus = Constant.stopped;
});
});
return _androidPedestrianController.stream;
}
/// Returns the steps taken since last system boot.
/// Events may come with a delay.
@override
Stream<StepCount> get stepCountStream => _stepCountChannel
.receiveBroadcastStream()
.map((event) => StepCount.fromEvent(event));
}
二, 鸿蒙实现
dart部分没有写代码,在yaml里面配置了接口的插件对象
flutter:
plugin:
implements: pedometer
platforms:
ohos:
package: io.flutter.plugins.pedometer
pluginClass: PedometerPlugin
鸿蒙原生层的PedometerPlugin.ets
import { AbilityPluginBinding, FlutterPlugin, FlutterPluginBinding } from '@ohos/flutter_ohos/index';
import EventChannel, {
EventSink,
StreamHandler
} from '@ohos/flutter_ohos/src/main/ets/plugin/common/EventChannel';
import Log from '@ohos/flutter_ohos/src/main/ets/util/Log';
import { sensor } from '@kit.SensorServiceKit';
import { BusinessError } from '@kit.BasicServicesKit';
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
import common from '@ohos.app.ability.common';
/**
* 日志标签
*/
const TAG = "PedometerPlugin";
/**
* 计步插件实现类
* 实现了Flutter插件接口,用于计步相关的操作
*/
export default class PedometerPlugin implements FlutterPlugin {
private context: common.Context | null = null;
/**
* 计步检测事件接收器
*/
private stepDetectionChannel: EventChannel | null = null;;
/**
* 计步器事件接收器
*/
private stepCountChannel: EventChannel | null = null;
/**
* 获取插件的唯一类名
* @returns 插件的唯一类名
*/
getUniqueClassName(): string {
return TAG;
}
/**
* 插件附加到引擎时调用
* 初始化方法通道和事件通道
* @param binding Flutter插件绑定对象
*/
onAttachedToEngine(binding: FlutterPluginBinding): void {
Log.i(TAG, "插件附加到引擎");
this.context = binding.getApplicationContext()
// 初始化方法通道
let stepDetectionHandler = new StepDetector()
this.stepDetectionChannel = new EventChannel(binding.getBinaryMessenger(), "step_detection")
this.stepDetectionChannel.setStreamHandler(stepDetectionHandler);
let stepCounterHandler = new StepCounter()
this.stepCountChannel = new EventChannel(binding.getBinaryMessenger(), "step_count")
this.stepCountChannel.setStreamHandler(stepCounterHandler);
Log.d(TAG, `系统亮度变化事件通道初始化完成:`);
}
/**
* 插件从引擎分离时调用
* 清理方法通道和事件通道
* @param binding Flutter插件绑定对象
*/
onDetachedFromEngine(binding: FlutterPluginBinding): void {
Log.i(TAG, "插件从引擎分离");
}
/**
* 插件附加到Ability时调用
* 保存Ability绑定对象,用于后续获取上下文
* @param binding Ability插件绑定对象
*/
onAttachedToAbility(binding: AbilityPluginBinding): void {
Log.i(TAG, "插件附加到Ability");
}
/**
* 插件从Ability分离时调用
* 清理Ability绑定对象和窗口对象
*/
onDetachedFromAbility(): void {
Log.i(TAG, "插件从Ability分离");
}
}
class StepDetector implements StreamHandler {
private eventSink: EventSink | null = null;
onListen(args: ESObject, events: EventSink): void {
this.eventSink = events;
try {
sensor.on(sensor.SensorId.PEDOMETER_DETECTION, (data: sensor.PedometerDetectionResponse) => {
console.info('Succeeded in invoking on. Pedometer scalar: ' + data.scalar);
events.success(data.scalar);
}, { interval: 100000000 });
} catch (error) {
let e: BusinessError = error as BusinessError;
events.error("1", e.message, `Failed to invoke on. Code: ${e.code}, message: ${e.message}`)
}}
onCancel(args: ESObject): void {
sensor.off(sensor.SensorId.PEDOMETER_DETECTION);
}
}
class StepCounter implements StreamHandler {
private eventSink: EventSink | null = null;
onListen(args: ESObject, events: EventSink): void {
this.eventSink = events;
try {
sensor.on(sensor.SensorId.PEDOMETER, (data: sensor.PedometerResponse) => {
console.info('Succeeded in invoking on. Step count: ' + data.steps);
events.success(data.steps);
}, { interval: 100000000 });
} catch (error) {
let e: BusinessError = error as BusinessError;
events.error("1", e.message, `Failed to invoke on. Code: ${e.code}, message: ${e.message}`)
}
}
onCancel(args: ESObject): void {
sensor.off(sensor.SensorId.PEDOMETER);
}
}
三,pedometer接入模块层
在ym里面把几个模块都放进来
dependencies:
flutter:
sdk: flutter
pedometer_interface:
path: ../pedometer_interface
pedometer_android:
path: ../pedometer_android
pedometer_ios:
path: ../pedometer_ios
pedometer_ohos:
path: ../pedometer_ohos #此处为添加
核心代码就是做每个平台的转发pedometer.dart
import 'package:pedometer_interface/pedometer_interface.dart';
export 'package:pedometer_interface/pedometer_interface.dart';
/// Plugin for changing screen brightness
class Pedometer {
/// ScreenBrightness designed as static method collection class
/// So constructor should not provide to user.
Pedometer._();
/// Plugin singleton
static final Pedometer _instance = Pedometer._();
/// Returns a singleton instance of [Pedometer].
///
/// [Pedometer] is designed to work as a singleton.
factory Pedometer() => instance;
/// Returns a singleton instance of [Pedometer].
///
/// [Pedometer] is designed to work as a singleton.
static Pedometer get instance => _instance;
/// Private platform prevent direct access or overriding
static PedometerPlatform get _platform {
return PedometerPlatform.instance;
}
/// Returns one step at a time.
/// Events come every time a step is detected.
static Stream<PedestrianStatus> get pedestrianStatusStream => _platform.pedestrianStatusStream;
/// Returns the steps taken since last system boot.
/// Events may come with a delay.
static Stream<StepCount> get stepCountStream => _platform.stepCountStream;
}
四,测试example
增加鸿蒙平台,并且在ohos里面的module.json5加入权限申请
{
"name" : "ohos.permission.ACTIVITY_MOTION",
"reason": "$string:app_name",
"usedScene": {
"abilities": [
"EntryAbility"
],
"when":"inuse"
}
}
运行到mater60pro上,截图如下
开源地址:
https://gitcode.com/astevencui/pedometer.git