react-native-volume-manager:基于 React Native for OpenHarmony 的鸿蒙音量管理适配项目

管理系统音量的 React Native 模块,支持 iOS/Android

Branch3Tags3
FilesLast commitLast update
19 days ago
19 days ago
19 days ago
1 month ago
1 month ago
19 days ago
19 days ago
19 days ago
1 month ago
19 days ago
1 month ago

react-native-volume-manager

本项目基于 react-native-volume-manager开发。如果在使用过程中有任何问题,欢迎在AtomGit提交Issue,会及时跟进。

项目介绍

@hxa-rn/react-native-volume-manager 是 react-native-volume-manager 2.0.8 的 HarmonyOS 适配包,面向 React Native for OpenHarmony 应用提供系统音量读取、音量变化监听、铃声模式读取与监听、设备静音状态查询等能力。

本包包含 TurboModule 原生实现,通过 @kit.AudioKit 访问 HarmonyOS 音频能力。音频会话、静音兼容及前台媒体音量写入采用明确的近似映射;铃声模式写入、DND 授权等能力继续受控降级。

集成指南

npm install @hxa-rn/react-native-volume-manager

peerDependencies:react、react-native(>= 0.72)。

包内声明了 harmony.alias: "react-native-volume-manager"。业务代码继续从原包名导入:

import { VolumeManager, getVolume } from 'react-native-volume-manager';

工程支持 RNOH Autolinking 时无需手工注册。若项目未启用 Autolinking,需要将包内 volume_manager.har 接入 Harmony 工程,并在 C++ 与 ArkTS 两侧注册 VolumeManagerPackage。

如需使用 HarmonyOS 的 setVolume / showNativeVolumeUI 近似能力,还必须在应用 ArkUI 页面挂载宿主:

// ArkTS(与 RNApp 同级)
import { VolumePanelHost } from '@hxa-rn/react-native-volume-manager';

VolumePanelHost()

宿主必须挂载。默认关闭系统音量条,并挂 (-1,-1) 抑制硬件键系统条。setVolume(..., { showUI: true }) 只让本次写入弹出官方条(不传 position)。showNativeVolumeUI({ enabled }) 不改音量、也不立刻出条,只决定之后按硬件音量键是否出条。

使用说明

import React, { useEffect, useState } from 'react';
import { Button, Text, View } from 'react-native';
import {
  addVolumeListener,
  getRingerMode,
  getVolume,
  isAndroidDeviceSilent,
} from 'react-native-volume-manager';

export default function VolumeExample() {
  const [message, setMessage] = useState('待读取');

  useEffect(() => {
    const subscription = addVolumeListener((event) => {
      setMessage(`音量事件:${event.volume} / ${event.type ?? 'unknown'}`);
    });
    return () => subscription.remove();
  }, []);

  const readStatus = async () => {
    try {
      const volume = await getVolume();
      const ringerMode = await getRingerMode();
      const silent = await isAndroidDeviceSilent();
      setMessage(
        `音量=${volume.volume.toFixed(2)},铃声模式=${ringerMode},静音=${silent}`
      );
    } catch (error) {
      setMessage(`读取失败:${String(error)}`);
    }
  };

  return (
    <View>
      <Button title="读取音频状态" onPress={readStatus} />
      <Text>{message}</Text>
    </View>
  );
}
import { VolumeManager } from 'react-native-volume-manager';

export async function adjustMediaVolume(value: number, showUI: boolean) {
  await VolumeManager.setVolume(value, { showUI });
  return VolumeManager.getVolume();
}

export async function allowHardwareVolumeHud(enabled: boolean) {
  await VolumeManager.showNativeVolumeUI({ enabled });
}

铃声模式监听必须成对释放:

import {
  addRingerListener,
  removeRingerListener,
} from 'react-native-volume-manager';

const subscription = addRingerListener((event) => {
  console.log(event.status, event.mode);
});

removeRingerListener(subscription);
import React from 'react';
import { View, Text, Button } from 'react-native';
import {
  useRingerMode,
  RINGER_MODE,
} from 'react-native-volume-manager';

const modeText = {
  [RINGER_MODE.silent]: 'Silent',
  [RINGER_MODE.normal]: 'Normal',
  [RINGER_MODE.vibrate]: 'Vibrate',
};

export default function RingerExample() {
  const { mode, error, setMode } = useRingerMode();

  return (
    <View>
      <Text>Ringer Mode: {mode !== undefined ? modeText[mode] : null}</Text>
      <Button title="Silent" onPress={() => setMode(RINGER_MODE.silent)} />
      <Button title="Normal" onPress={() => setMode(RINGER_MODE.normal)} />
      <Button title="Vibrate" onPress={() => setMode(RINGER_MODE.vibrate)} />
      <Text>{error?.message}</Text>
    </View>
  );
}
import React from 'react';
import { View, Text } from 'react-native';
import { useSilentSwitch } from 'react-native-volume-manager';

export default function SilentSwitchExample() {
  const status = useSilentSwitch();

  return (
    <View>
      {status ? (
        <View>
          <Text>Is Muted: {status.isMuted ? 'YES' : 'NO'}</Text>
          <Text>Is Initial Query: {status.initialQuery ? 'YES' : 'NO'}</Text>
        </View>
      ) : (
        <Text>Fetching...</Text>
      )}
    </View>
  );
}

接口文档

读取与监听

接口 参数 返回值 HarmonyOS 说明
getVolume() 无 Promise<VolumeResult> 返回主音量及可用音频流的归一化值
addVolumeListener(callback) 音量事件回调 EmitterSubscription 监听 RNVMEventVolume
getRingerMode() 无 Promise<0 | 1 | 2 | undefined> 0 静音、1 振动、2 正常
isAndroidDeviceSilent() 无 Promise<boolean | null> 基于铃声模式和媒体音量判断
addRingerListener(callback) 铃声事件回调 订阅对象 监听 RNVMSilentEvent;鸿蒙事件 mode 为 SILENT / VIBRATE / NORMAL,不含 MUTED
removeRingerListener(subscription) 订阅对象 void 停止铃声事件转发并移除 JS 订阅

写入与平台专属接口

接口 参数 返回值 HarmonyOS 说明
setVolume(value, config?) value: 0–1,config: {playSound?, type?, showUI?} Promise<void> 要求挂载 Host;映射到前台 MEDIA 原始音量等级;showUI 只控制本次写入是否弹出官方音量条
setRingerMode(mode) 0 | 1 | 2 Promise<0 | 1 | 2 | undefined> 受控降级,返回请求值
checkDndAccess() 无 Promise<boolean | undefined> 返回 false
requestDndAccess() 无 Promise<boolean | undefined> 返回 false,不跳转
showNativeVolumeUI(config) {enabled: boolean} Promise<void> 不改音量、不立刻出条;enabled=true 拆掉 (-1,-1) 抑制器,之后按音量键能出系统条;enabled=false 再挂抑制器
enable(enabled?, async?) 布尔值 Promise<void> 近似映射音频会话激活/停用;启用采用混音策略
setActive(value?, async?) 布尔值 Promise<void> 近似映射 activateAudioSession / deactivateAudioSession
setCategory(value, mixWithOthers?) 音频会话类别 Promise<void> 近似映射 DEFAULT / MIX_WITH_OTHERS / PAUSE_OTHERS 并发策略
setMode(value) 音频会话模式 Promise<void> API20 映射 MEDIA / GAME / VOICE_COMMUNICATION 场景
enableInSilenceMode(enabled?) 布尔值 Promise<void> iOS 专属,受控降级
addSilentListener(callback) 静音开关回调 订阅对象 返回铃声/媒体静音兼容事件 {isMuted, initialQuery}
setNativeSilenceCheckInterval(value) 毫秒数 void 事件驱动,无轮询需求,受控降级

Hooks、常量与类型

名称 说明
useRingerMode() 读取铃声模式并提供设置函数;HarmonyOS 设置操作为受控降级
useSilentSwitch(interval?) iOS 为物理静音开关;HarmonyOS 返回铃声/媒体静音兼容状态
RINGER_MODE { silent: 0, vibrate: 1, normal: 2 }
AndroidVolumeTypes music / call / system / ring / alarm / notification
VolumeResult 至少包含 volume,音量事件可包含 type
Mode 上游类型含 SILENT / VIBRATE / NORMAL / MUTED;鸿蒙铃声事件不写 MUTED

快速验证(运行 Example)

前置条件

依赖 版本要求
Node.js >= 18
DevEco Studio 5.0+ / 6.0+
HarmonyOS SDK API 12+

运行步骤

1. 克隆仓库

git clone https://gitcode.com/hxa-rn/react-native-volume-manager.git
cd react-native-volume-manager
git checkout main

2. 安装仓库开发依赖

npm install --legacy-peer-deps

Example 已改为从 npm 公仓安装 @hxa-rn/react-native-volume-manager@2.0.11,不再使用本地 file:../xxx.tgz,运行 Example 不必再执行 npm pack。

3. 进入 example 目录,安装依赖

cd example
npm install --legacy-peer-deps

4. 生成 JS Bundle

npm run dev

产物:harmony/entry/src/main/resources/rawfile/bundle.harmony.js

5. 用 DevEco Studio 打开鸿蒙工程

  • 打开 DevEco Studio
  • 选择 example/harmony 目录
  • 等待 Sync 完成

6. 编译并运行 HAP

在 DevEco Studio 中点击运行按钮,将 HAP 安装到设备/模拟器。

注意:Example 中已预置插件依赖和 Package 注册,无需手动配置 Link。

约束与限制

项目 要求或限制
包版本 2.0.11
React Native >= 0.72
Node.js >= 18
HarmonyOS SDK compatibleSdkVersion 6.0.0(20),API 20+
权限 无额外系统权限
能力 HarmonyOS 行为
getVolume、addVolumeListener 原生实现;返回 0–1 归一化音量
getRingerMode、addRingerListener、isAndroidDeviceSilent 原生实现
setVolume 通过已挂载的 VolumePanelHost 近似调节前台 MEDIA 音量;showUI 只控制本次是否弹出官方音量条
setRingerMode 受控降级;第三方应用不能直接设置铃声模式
checkDndAccess、requestDndAccess 无等价公开 API,返回 false,不跳转设置页
showNativeVolumeUI 只记后续硬件键策略,不改音量、不立刻出条;enabled=false 用 (-1,-1) 抑制硬件键系统条
enable、setActive、setCategory、setMode 映射 HarmonyOS AudioSessionManager,属于近似实现
addSilentListener、useSilentSwitch 以铃声模式与媒体静音状态模拟,不代表物理静音开关
enableInSilenceMode、setNativeSilenceCheckInterval 无等价语义,安全 no-op

system 与 notification 没有独立的 HarmonyOS 音量类型,当前映射到最接近的 RINGTONE 流。API20 下音量与铃声监听均可在订阅移除时真实注销;应用仍应在 React 组件卸载时释放 JS 订阅。

HarmonyOS 不允许三方应用通过命令式 API 静默改系统音量。本包使用官方 AVVolumePanel:Host 保持挂载,默认挂 (-1,-1) 抑制器。setVolume(..., { showUI: true }) 本次出官方条(不传 position,通常在右上角);showNativeVolumeUI 不立刻出条,只决定之后硬件键是否出条。该方案仅支持前台 MEDIA 音量,非 music 类型会映射到 MEDIA;后台调节和按流精确写入仍不可用。官方条显示时可能吞掉部分点击。

开源license

本项目基于 MIT 协议,详见 LICENSE 文件。

问题反馈渠道

欢迎在 AtomGit 提交 Issue。也可通过以下地址反馈:

Introduction

管理系统音量的 React Native 模块,支持 iOS/Android

Customize your domain