flutter鸿蒙重写pedometer实现步行监听和步数记录

由于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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值