forEachForeground/forEachBackground 此前扇出但多数 Provider 未实现 → 非激活槽 native 监听仍跑。补齐:
- ShakeProvider:onBackground 停传感器、onForeground 按 H5 意图(wantShake)恢复;区分 H5 请求态与实际监听态
- LocationProvider:onBackground 停连续定位、onForeground 按 wantContinuous 恢复(单次定位无需恢复)
- NetworkProvider:onBackground 注销网络订阅、onForeground 重订阅(抽 unsubscribe)
- AudioProvider:onBackground 停语音/音效播放(非激活端不出声)
至此"同时只有一个激活"在 native 侧也落实:去激活槽停传感器/定位/网络/音频;H5 侧由 appservice('2') 自停。
WeChat resp 路由保持现状(微信整体延期;双 Web 下发起槽持续存活,边缘情形见 m3-deferred 记录)。
devecocli build + scripts/test.sh 通过。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
121 lines
4.0 KiB
Plaintext
121 lines
4.0 KiB
Plaintext
/**
|
||
* 摇一摇能力(契约 §8.5/§9,T-M3-08)。@kit.SensorServiceKit 加速度计。
|
||
* - startshake:开始监听;检测到摇动后约 1 秒出站 shakeEnd(空串)。
|
||
* - SwitchShake:data="1" 开声音/震动反馈 / 其他关。
|
||
* - stopshake:停止监听。
|
||
* 需 module.json5 声明 ohos.permission.ACCELEROMETER。
|
||
*/
|
||
import { sensor } from '@kit.SensorServiceKit';
|
||
import { vibrator } from '@kit.SensorServiceKit';
|
||
import { BusinessError } from '@kit.BasicServicesKit';
|
||
import { BridgeController } from 'feature_bridge';
|
||
import { InboundHandlers, OutboundHandlers } from 'contracts';
|
||
import { Logger } from 'common';
|
||
import { CapabilityContext, CapabilityProvider } from '../core/CapabilityProvider';
|
||
|
||
/** 摇动阈值(合加速度 m/s²;静止约 9.8,摇动易超 20)。 */
|
||
const SHAKE_THRESHOLD: number = 20;
|
||
/** 上报周期 100ms。 */
|
||
const INTERVAL_NS: number = 100000000;
|
||
/** shakeEnd 延迟(约 1 秒)。 */
|
||
const SHAKE_END_DELAY: number = 1000;
|
||
|
||
export class ShakeProvider implements CapabilityProvider {
|
||
readonly name: string = 'shake';
|
||
private readonly log: Logger = Logger.tag('ShakeProvider');
|
||
private bridge: BridgeController | undefined = undefined;
|
||
private listening: boolean = false;
|
||
private feedbackOn: boolean = false;
|
||
private pending: boolean = false;
|
||
private endTimer: number = -1;
|
||
private wantShake: boolean = false;
|
||
private readonly onAccel: (data: sensor.AccelerometerResponse) => void;
|
||
|
||
constructor() {
|
||
this.onAccel = (data: sensor.AccelerometerResponse) => this.handleAccel(data);
|
||
}
|
||
|
||
register(bridge: BridgeController, _ctx: CapabilityContext): void {
|
||
this.bridge = bridge;
|
||
bridge.registerHandler(InboundHandlers.StartShake, (_d: string, _cb: (resp: string) => void) => {
|
||
this.wantShake = true;
|
||
this.start();
|
||
});
|
||
bridge.registerHandler(InboundHandlers.SwitchShake, (data: string, _cb: (resp: string) => void) => {
|
||
this.feedbackOn = data === '1';
|
||
});
|
||
bridge.registerHandler(InboundHandlers.StopShake, (_d: string, _cb: (resp: string) => void) => {
|
||
this.wantShake = false;
|
||
this.stop();
|
||
});
|
||
}
|
||
|
||
/** 槽激活:若 H5 仍要摇一摇则恢复监听(M5 §7.5)。 */
|
||
onForeground(): void {
|
||
if (this.wantShake) {
|
||
this.start();
|
||
}
|
||
}
|
||
|
||
/** 槽去激活:停传感器监听(保留 H5 意图,激活时恢复)。 */
|
||
onBackground(): void {
|
||
this.stop();
|
||
}
|
||
|
||
onDestroy(): void {
|
||
this.wantShake = false;
|
||
this.stop();
|
||
}
|
||
|
||
private start(): void {
|
||
if (this.listening) {
|
||
return;
|
||
}
|
||
try {
|
||
sensor.on(sensor.SensorId.ACCELEROMETER, this.onAccel, { interval: INTERVAL_NS });
|
||
this.listening = true;
|
||
} catch (e) {
|
||
this.log.w(`sensor.on failed: ${(e as BusinessError).message}`);
|
||
}
|
||
}
|
||
|
||
private stop(): void {
|
||
if (!this.listening) {
|
||
return;
|
||
}
|
||
try {
|
||
sensor.off(sensor.SensorId.ACCELEROMETER, this.onAccel);
|
||
} catch (e) {
|
||
this.log.w(`sensor.off failed: ${(e as BusinessError).message}`);
|
||
}
|
||
this.listening = false;
|
||
// 取消未决的 shakeEnd 计时并复位节流,避免停止后仍触发出站 / 重启后首次摇动被吞
|
||
if (this.endTimer !== -1) {
|
||
clearTimeout(this.endTimer);
|
||
this.endTimer = -1;
|
||
}
|
||
this.pending = false;
|
||
}
|
||
|
||
private handleAccel(data: sensor.AccelerometerResponse): void {
|
||
if (this.pending) {
|
||
return;
|
||
}
|
||
const magnitude: number = Math.sqrt(data.x * data.x + data.y * data.y + data.z * data.z);
|
||
if (magnitude < SHAKE_THRESHOLD) {
|
||
return;
|
||
}
|
||
// 摇动触发:节流;可选反馈震动;约 1 秒后出站 shakeEnd
|
||
this.pending = true;
|
||
if (this.feedbackOn) {
|
||
vibrator.startVibration({ type: 'time', duration: 200 }, { usage: 'notification' })
|
||
.catch((e: BusinessError) => this.log.w(`shake vibrate failed: ${e.message}`));
|
||
}
|
||
this.endTimer = setTimeout(() => {
|
||
this.endTimer = -1;
|
||
this.bridge?.callHandler(OutboundHandlers.ShakeEnd, '');
|
||
this.pending = false;
|
||
}, SHAKE_END_DELAY);
|
||
}
|
||
}
|